diff --git a/CursorLang/Services/ILocalizationService.cs b/CursorLang/Services/ILocalizationService.cs index 16efc3f..6bab8a0 100644 --- a/CursorLang/Services/ILocalizationService.cs +++ b/CursorLang/Services/ILocalizationService.cs @@ -5,7 +5,11 @@ namespace CursorLang.Services; /// Язык интерфейса для выбора в настройках. /// Код культуры: «ru», «en». /// Название на самом этом языке. -public sealed record LanguageOption(string Code, string DisplayName); +public sealed record LanguageOption(string Code, string DisplayName) +{ + // Средства доступности берут имя элемента списка отсюда + public override string ToString() => DisplayName; +} /// /// Даёт строки интерфейса и умеет менять язык без перезапуска. diff --git a/CursorLang/ViewModels/SettingsViewModel.cs b/CursorLang/ViewModels/SettingsViewModel.cs index 3b55be6..97506ed 100644 --- a/CursorLang/ViewModels/SettingsViewModel.cs +++ b/CursorLang/ViewModels/SettingsViewModel.cs @@ -7,8 +7,32 @@ using CursorLang.Services; namespace CursorLang.ViewModels; -/// Вариант выбора в списке: значение и его подпись на текущем языке. -public sealed record EnumOption(T Value, string Display); +/// +/// Вариант выбора в списке. Подпись меняется вместе с языком, а сам объект +/// живёт всё время работы окна: если пересоздавать элементы списка, +/// ComboBox сбрасывает выбранное значение. +/// +public sealed class EnumOption : ObservableObject where T : struct, Enum +{ + private string _display; + + public EnumOption(T value, string display) + { + Value = value; + _display = display; + } + + public T Value { get; } + + public string Display + { + get => _display; + set => SetProperty(ref _display, value); + } + + // Средства доступности берут имя элемента списка отсюда + public override string ToString() => Display; +} /// /// Окно настроек. Значения правятся прямо в , @@ -38,7 +62,9 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable Settings.PropertyChanged += OnSettingsChanged; Localization.PropertyChanged += OnLocalizationChanged; - BuildLocalizedOptions(); + PlacementModes = CreateOptions(); + CursorCorners = CreateOptions(); + ScreenPositions = CreateOptions(); } public AppSettings Settings { get; } @@ -47,11 +73,11 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable public IReadOnlyList BackgroundPalette { get; } = Palette; - public IReadOnlyList> PlacementModes { get; private set; } = []; + public IReadOnlyList> PlacementModes { get; } - public IReadOnlyList> CursorCorners { get; private set; } = []; + public IReadOnlyList> CursorCorners { get; } - public IReadOnlyList> ScreenPositions { get; private set; } = []; + public IReadOnlyList> ScreenPositions { get; } public void Dispose() { @@ -68,26 +94,31 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable } // Подписи вариантов приходят из ресурсов, поэтому при смене языка - // списки нужно собрать заново — привязки сами перечитают их + // обновляем только текст — сами элементы списков остаются прежними private void OnLocalizationChanged(object? sender, PropertyChangedEventArgs e) { - if (e.PropertyName == Binding.IndexerName) + if (e.PropertyName != Binding.IndexerName) { - BuildLocalizedOptions(); + return; + } + + Translate(PlacementModes); + Translate(CursorCorners); + Translate(ScreenPositions); + } + + private EnumOption[] CreateOptions() where T : struct, Enum => + [.. Enum.GetValues().Select(value => new EnumOption(value, GetDisplayName(value)))]; + + private void Translate(IReadOnlyList> options) where T : struct, Enum + { + foreach (EnumOption option in options) + { + option.Display = GetDisplayName(option.Value); } } - private void BuildLocalizedOptions() - { - PlacementModes = BuildOptions(nameof(PopupPlacementMode)); - CursorCorners = BuildOptions(nameof(CursorCorner)); - ScreenPositions = BuildOptions(nameof(ScreenPosition)); - - OnPropertyChanged(nameof(PlacementModes)); - OnPropertyChanged(nameof(CursorCorners)); - OnPropertyChanged(nameof(ScreenPositions)); - } - - private EnumOption[] BuildOptions(string resourcePrefix) where T : struct, Enum => - [.. Enum.GetValues().Select(value => new EnumOption(value, Localization[$"{resourcePrefix}_{value}"]))]; + // Ключ ресурса собирается из имени типа и значения: PopupPlacementMode_AtCursor + private string GetDisplayName(T value) where T : struct, Enum => + Localization[$"{typeof(T).Name}_{value}"]; }