fixed bug drop list is empty after language switch

This commit is contained in:
2026-08-08 21:55:34 +05:00
parent d146fd442a
commit 1fdfe77d78
2 changed files with 58 additions and 23 deletions
+5 -1
View File
@@ -5,7 +5,11 @@ namespace CursorLang.Services;
/// <summary>Язык интерфейса для выбора в настройках.</summary> /// <summary>Язык интерфейса для выбора в настройках.</summary>
/// <param name="Code">Код культуры: «ru», «en».</param> /// <param name="Code">Код культуры: «ru», «en».</param>
/// <param name="DisplayName">Название на самом этом языке.</param> /// <param name="DisplayName">Название на самом этом языке.</param>
public sealed record LanguageOption(string Code, string DisplayName); public sealed record LanguageOption(string Code, string DisplayName)
{
// Средства доступности берут имя элемента списка отсюда
public override string ToString() => DisplayName;
}
/// <summary> /// <summary>
/// Даёт строки интерфейса и умеет менять язык без перезапуска. /// Даёт строки интерфейса и умеет менять язык без перезапуска.
+53 -22
View File
@@ -7,8 +7,32 @@ using CursorLang.Services;
namespace CursorLang.ViewModels; namespace CursorLang.ViewModels;
/// <summary>Вариант выбора в списке: значение и его подпись на текущем языке.</summary> /// <summary>
public sealed record EnumOption<T>(T Value, string Display); /// Вариант выбора в списке. Подпись меняется вместе с языком, а сам объект
/// живёт всё время работы окна: если пересоздавать элементы списка,
/// ComboBox сбрасывает выбранное значение.
/// </summary>
public sealed class EnumOption<T> : 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;
}
/// <summary> /// <summary>
/// Окно настроек. Значения правятся прямо в <see cref="AppSettings"/>, /// Окно настроек. Значения правятся прямо в <see cref="AppSettings"/>,
@@ -38,7 +62,9 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable
Settings.PropertyChanged += OnSettingsChanged; Settings.PropertyChanged += OnSettingsChanged;
Localization.PropertyChanged += OnLocalizationChanged; Localization.PropertyChanged += OnLocalizationChanged;
BuildLocalizedOptions(); PlacementModes = CreateOptions<PopupPlacementMode>();
CursorCorners = CreateOptions<CursorCorner>();
ScreenPositions = CreateOptions<ScreenPosition>();
} }
public AppSettings Settings { get; } public AppSettings Settings { get; }
@@ -47,11 +73,11 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable
public IReadOnlyList<Color> BackgroundPalette { get; } = Palette; public IReadOnlyList<Color> BackgroundPalette { get; } = Palette;
public IReadOnlyList<EnumOption<PopupPlacementMode>> PlacementModes { get; private set; } = []; public IReadOnlyList<EnumOption<PopupPlacementMode>> PlacementModes { get; }
public IReadOnlyList<EnumOption<CursorCorner>> CursorCorners { get; private set; } = []; public IReadOnlyList<EnumOption<CursorCorner>> CursorCorners { get; }
public IReadOnlyList<EnumOption<ScreenPosition>> ScreenPositions { get; private set; } = []; public IReadOnlyList<EnumOption<ScreenPosition>> ScreenPositions { get; }
public void Dispose() public void Dispose()
{ {
@@ -68,26 +94,31 @@ public sealed partial class SettingsViewModel : ObservableObject, IDisposable
} }
// Подписи вариантов приходят из ресурсов, поэтому при смене языка // Подписи вариантов приходят из ресурсов, поэтому при смене языка
// списки нужно собрать заново — привязки сами перечитают их // обновляем только текст — сами элементы списков остаются прежними
private void OnLocalizationChanged(object? sender, PropertyChangedEventArgs e) 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<T>[] CreateOptions<T>() where T : struct, Enum =>
[.. Enum.GetValues<T>().Select(value => new EnumOption<T>(value, GetDisplayName(value)))];
private void Translate<T>(IReadOnlyList<EnumOption<T>> options) where T : struct, Enum
{
foreach (EnumOption<T> option in options)
{
option.Display = GetDisplayName(option.Value);
} }
} }
private void BuildLocalizedOptions() // Ключ ресурса собирается из имени типа и значения: PopupPlacementMode_AtCursor
{ private string GetDisplayName<T>(T value) where T : struct, Enum =>
PlacementModes = BuildOptions<PopupPlacementMode>(nameof(PopupPlacementMode)); Localization[$"{typeof(T).Name}_{value}"];
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}"]))];
} }