diff --git a/StarterNG/Classes/Settings.cs b/StarterNG/Classes/Settings.cs index c90d8a2..6a36eaa 100644 --- a/StarterNG/Classes/Settings.cs +++ b/StarterNG/Classes/Settings.cs @@ -72,8 +72,6 @@ public sealed class Settings public int Width = 1280; // width public int Height = 720; // height public int BufferScalePercent = 100; // starter.bufferscale → gfx.framebuffer.fidelity - /// 0 = Reinhard, 1 = ACESFilm (Pascal ChangeHDR). - public int HdrToneMap; // starter.hdr public int MaxTextureSize = 4096; // maxtexturesize public int MaxCabTextureSize = 4096; // maxcabtexturesize public int TextureFiltering = 8; // anisotropicfiltering (1/2/4/8/16) @@ -394,15 +392,14 @@ public sealed class Settings _config.Remove("gfx.framebuffer.fidelity"); } - /// Copy Reinhard/ACESFilm GLSL into shaders/tonemapping.glsl when present. + /// Copy the Reinhard GLSL into shaders/tonemapping.glsl when present. public void ApplyHdrShader() { // Only meaningful for full / legacy / experimental renderers (Pascal). if (RenderEngine is not (0 or 1 or 5)) return; - string name = HdrToneMap == 0 ? "Reinhard.glsl" : "ACESFilm.glsl"; - string? src = FindBundled(name); + string? src = FindBundled("Reinhard.glsl"); if (src is null) return; try @@ -495,7 +492,6 @@ public sealed class Settings DebugMode = c.GetBool("debugmode", false); VirtualShunting = c.GetBool("ai.trainman", true); LogMissingVehicleFiles = c.GetBool("starter.logmissingvehicles", false); - HdrToneMap = Clamp(c.GetInt("starter.hdr", 0), 0, 1); // Graphics RenderEngine = IndexOf(RenderEngines, c.GetString("gfxrenderer", "full"), 0); @@ -621,7 +617,6 @@ public sealed class Settings c.SetBool("debugmode", DebugMode); c.SetBool("ai.trainman", VirtualShunting); c.SetBool("starter.logmissingvehicles", LogMissingVehicleFiles); - c.SetInt("starter.hdr", Clamp(HdrToneMap, 0, 1)); // Graphics c.Set("gfxrenderer", RenderEngines[Clamp(RenderEngine, 0, RenderEngines.Length - 1)]); diff --git a/StarterNG/MainWindow.axaml.cs b/StarterNG/MainWindow.axaml.cs index 24e539f..fa6d87f 100644 --- a/StarterNG/MainWindow.axaml.cs +++ b/StarterNG/MainWindow.axaml.cs @@ -45,6 +45,7 @@ public partial class MainWindow : Window TopBar.Padding = new Thickness(0, TitleBarHeight, 0, 0); PopulateLangCombo(); + App.Loc.LanguageChanged += OnLanguageChanged; // Keep Start enabled/caption in sync with selection (Pascal actStartUpdate). AppState.Instance.Changed += UpdateStartButton; @@ -79,39 +80,56 @@ public partial class MainWindow : Window SettingsView.IsVisible = page == "settings"; } + // One entry per file in lang/. The entries are language names, which are not + // translated, so this runs once - never from OnLanguageChanged, where it + // would clear the combo from inside its own SelectionChanged and throw. private void PopulateLangCombo() { LangCombo.Items.Clear(); - int selected = 0; - var langs = LocalizationService.AvailableLanguages(); - if (langs.Count == 0) - { - LangCombo.Items.Add(new ComboBoxItem { Content = "English" }); - LangCombo.SelectedIndex = 0; - return; - } - for (int i = 0; i < langs.Count; i++) - { - LangCombo.Items.Add(new ComboBoxItem { Content = langs[i].Name, Tag = langs[i].Code }); - if (string.Equals(langs[i].Name, App.Loc.CurrentLanguage, StringComparison.OrdinalIgnoreCase) || - string.Equals(langs[i].Code, App.Loc.CurrentLangCode, StringComparison.OrdinalIgnoreCase)) - selected = i; - } - LangCombo.SelectedIndex = selected; + foreach (var lang in LocalizationService.AvailableLanguages()) + LangCombo.Items.Add(new ComboBoxItem { Content = lang.Name, Tag = lang.Code }); + if (LangCombo.ItemCount == 0) + LangCombo.Items.Add(new ComboBoxItem { Content = "English", Tag = "en" }); + + SelectActiveLanguage(); + } + + // Points the combo at the loaded language; a no-op when it already shows it, + // which is the case for a pick made here. + private void SelectActiveLanguage() + { + var item = LangCombo.Items.OfType() + .FirstOrDefault(it => string.Equals(it.Tag as string, App.Loc.CurrentLangCode, + StringComparison.OrdinalIgnoreCase)) + ?? LangCombo.Items.OfType().FirstOrDefault(); + + if (item != null && !ReferenceEquals(LangCombo.SelectedItem, item)) + LangCombo.SelectedItem = item; } private void LangCombo_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) { - if (sender is not ComboBox { SelectedItem: ComboBoxItem item }) return; - if (!item.IsKeyboardFocusWithin) return; // ignore the programmatic initial set - var lang = item.Tag as string ?? item.Content?.ToString(); - if (!string.IsNullOrEmpty(lang)) - { - App.ApplyLanguage(lang); - ScenariosView?.RebuildAfterLanguageChange(); - DepotView?.RebuildAfterLanguageChange(); - UpdateStartButton(); - } + if ((LangCombo.SelectedItem as ComboBoxItem)?.Tag is not string code) + return; + + // Filling the combo in selects the language that is already loaded, which + // lands here as well - comparing against the loaded one is what tells the + // two apart, and it keeps the handler idempotent. + if (string.Equals(code, App.Loc.CurrentLangCode, StringComparison.OrdinalIgnoreCase)) + return; + + App.ApplyLanguage(code); + } + + // Captions built in code (train stats, panel headers, combo items) are read + // from App.Loc once, so they have to be built again. Runs for a pick made + // here or in the settings tab, hence hanging off Loc rather off the combo. + private void OnLanguageChanged() + { + SelectActiveLanguage(); + ScenariosView?.RebuildAfterLanguageChange(); + DepotView?.RebuildAfterLanguageChange(); + UpdateStartButton(); } private void HelpButton_OnClick(object? sender, RoutedEventArgs e) diff --git a/StarterNG/Services/LocalizationService.cs b/StarterNG/Services/LocalizationService.cs index 72179ec..da8ec71 100644 --- a/StarterNG/Services/LocalizationService.cs +++ b/StarterNG/Services/LocalizationService.cs @@ -29,6 +29,13 @@ public class LocalizationService : INotifyPropertyChanged public event PropertyChangedEventHandler? PropertyChanged; + /// + /// Raised after a language file is loaded. XAML bindings refresh themselves + /// through ; this is for captions that code + /// builds once and has to build again. + /// + public event Action? LanguageChanged; + /// Display name of the active language (e.g. "English", "Polski"). public string CurrentLanguage { get; private set; } = "English"; @@ -113,7 +120,11 @@ public class LocalizationService : INotifyPropertyChanged CurrentLanguage = "English"; } - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item")); + // "Item[]" is the indexer name bindings listen for; the empty name asks + // every binding on this object to re-read. + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]")); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty)); + LanguageChanged?.Invoke(); return CurrentLanguage; } diff --git a/StarterNG/Views/Settings.axaml b/StarterNG/Views/Settings.axaml index 82f8a45..914c747 100644 --- a/StarterNG/Views/Settings.axaml +++ b/StarterNG/Views/Settings.axaml @@ -26,10 +26,9 @@ @@ -130,13 +129,6 @@ - - + + + + - @@ -270,11 +267,6 @@ - - - - diff --git a/StarterNG/Views/Settings.axaml.cs b/StarterNG/Views/Settings.axaml.cs index 156b423..9eb884d 100644 --- a/StarterNG/Views/Settings.axaml.cs +++ b/StarterNG/Views/Settings.axaml.cs @@ -12,6 +12,7 @@ using Avalonia.Layout; using Avalonia.Markup.Xaml.Styling; using Avalonia.Media; using StarterNG.Classes; +using StarterNG.Services; // Disambiguate from Avalonia.Input.KeyBinding (a command-input binding) imported above. using KeyBinding = StarterNG.Classes.KeyBinding; @@ -34,11 +35,10 @@ public partial class Settings : UserControl shaderResolutionSlider_OnValueChanged(null, null); }; - ChangeLanguageCb.SelectedIndex = App.Loc.CurrentLanguage switch - { - "Polski" => 0, - _ => 1 - }; + FillLanguageCombo(); + // The other picker is in the main window's top bar; both follow whatever + // language ends up loaded. + App.Loc.LanguageChanged += SelectActiveLanguage; FeedbackCb.SelectionChanged += FeedbackCb_OnSelectionChanged; FpsLimitEnableCb.IsCheckedChanged += (_, _) => FpsLimitSlider.IsEnabled = IsChecked(FpsLimitEnableCb); @@ -143,7 +143,7 @@ public partial class Settings : UserControl PopulateExeCombo(); // General - ChangeLanguageCb.SelectedIndex = s.Language == "Polski" ? 0 : 1; + SelectActiveLanguage(); FullscreenCb.IsChecked = s.Fullscreen; PauseInactiveCb.IsChecked = s.PauseWhenInactive; PauseStartCb.IsChecked = s.PauseOnStart; @@ -177,7 +177,6 @@ public partial class Settings : UserControl // Graphics RenderEngineCb.SelectedIndex = s.RenderEngine; - HdrToneMapCb.SelectedIndex = Clamp(s.HdrToneMap, 0, 1); SelectResolution(s.Width, s.Height); bufferScale.Value = s.BufferScalePercent; textureResolutionSlider.Value = Log2(s.MaxTextureSize, 12); @@ -264,7 +263,10 @@ public partial class Settings : UserControl var s = StarterNG.Classes.Settings.Instance; // General - s.Language = ChangeLanguageCb.SelectedIndex == 0 ? "Polski" : "English"; + // Keep the loaded language rather than writing a wrong one over it when + // the combo has no valid pick. + s.Language = (ChangeLanguageCb.SelectedItem as ComboBoxItem)?.Content?.ToString() + ?? App.Loc.CurrentLanguage; s.Fullscreen = IsChecked(FullscreenCb); s.PauseWhenInactive = IsChecked(PauseInactiveCb); s.PauseOnStart = IsChecked(PauseStartCb); @@ -297,7 +299,6 @@ public partial class Settings : UserControl // Graphics s.RenderEngine = Math.Max(0, RenderEngineCb.SelectedIndex); - s.HdrToneMap = Clamp(HdrToneMapCb.SelectedIndex, 0, 1); ReadResolution(s); s.BufferScalePercent = (int)bufferScale.Value; s.MaxTextureSize = 1 << (int)textureResolutionSlider.Value; @@ -454,14 +455,39 @@ public partial class Settings : UserControl shaderResolution.Text = $"{resolution} px"; } + // One entry per file in the lang/ folder, so every translation is reachable + // (the list used to be a hard-coded Polski / English pair). + private void FillLanguageCombo() + { + ChangeLanguageCb.Items.Clear(); + foreach (var lang in LocalizationService.AvailableLanguages()) + ChangeLanguageCb.Items.Add(new ComboBoxItem { Content = lang.Name, Tag = lang.Code }); + if (ChangeLanguageCb.ItemCount == 0) + ChangeLanguageCb.Items.Add(new ComboBoxItem { Content = "English", Tag = "en" }); + SelectActiveLanguage(); + } + + // Points the combo at the language currently loaded. The selection change + // lands in the handler below, which sees it is already loaded and stops. + private void SelectActiveLanguage() + { + var item = ChangeLanguageCb.Items.OfType() + .FirstOrDefault(it => string.Equals(it.Tag as string, App.Loc.CurrentLangCode, + StringComparison.OrdinalIgnoreCase)) + ?? ChangeLanguageCb.Items.OfType().FirstOrDefault(); + + if (item != null && !ReferenceEquals(ChangeLanguageCb.SelectedItem, item)) + ChangeLanguageCb.SelectedItem = item; + } + private void LanguageComboBox_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) { - if (sender is not ComboBox { SelectedItem: ComboBoxItem item }) return; - if (!item.IsKeyboardFocusWithin) return; // ignore programmatic (ApplyToUi) changes - var lang = item.Content?.ToString(); - if (string.IsNullOrEmpty(lang)) return; + if ((ChangeLanguageCb.SelectedItem as ComboBoxItem)?.Tag is not string code) + return; + if (string.Equals(code, App.Loc.CurrentLangCode, StringComparison.OrdinalIgnoreCase)) + return; - App.ApplyLanguage(lang); + App.ApplyLanguage(code); } private void FeedbackCb_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) => diff --git a/StarterNG/startercfg/lang/cz.xml b/StarterNG/startercfg/lang/cz.xml index 23191c3..8cc2e7a 100644 --- a/StarterNG/startercfg/lang/cz.xml +++ b/StarterNG/startercfg/lang/cz.xml @@ -223,7 +223,6 @@ Ano Ne Informace - Mapování tónů HDR Nastavení Soubor eu07.ini byl změněn mimo starter. Načíst jej znovu? Přidat celou kategorii diff --git a/StarterNG/startercfg/lang/en.xml b/StarterNG/startercfg/lang/en.xml index 747eeea..fc8ec32 100644 --- a/StarterNG/startercfg/lang/en.xml +++ b/StarterNG/startercfg/lang/en.xml @@ -223,7 +223,6 @@ Yes No Info - HDR tone mapping Settings eu07.ini was changed outside the starter. Reload it? Add whole category diff --git a/StarterNG/startercfg/lang/hu.xml b/StarterNG/startercfg/lang/hu.xml index 9149d40..0ff8c56 100644 --- a/StarterNG/startercfg/lang/hu.xml +++ b/StarterNG/startercfg/lang/hu.xml @@ -223,7 +223,6 @@ Igen Nem Információ - HDR tónusleképezés Beállítások Az eu07.ini fájl a starteren kívül módosult. Újratölti? Teljes kategória hozzáadása diff --git a/StarterNG/startercfg/lang/pl.xml b/StarterNG/startercfg/lang/pl.xml index e2215eb..3bc69cc 100644 --- a/StarterNG/startercfg/lang/pl.xml +++ b/StarterNG/startercfg/lang/pl.xml @@ -223,7 +223,6 @@ Tak Nie Informacje - Tone mapping HDR Ustawienia Plik eu07.ini został zmieniony poza starterem. Wczytać ponownie? Dodaj całą kategorię diff --git a/StarterNG/startercfg/lang/ru.xml b/StarterNG/startercfg/lang/ru.xml index a226348..8fff66c 100644 --- a/StarterNG/startercfg/lang/ru.xml +++ b/StarterNG/startercfg/lang/ru.xml @@ -223,7 +223,6 @@ Да Нет Информация - Тональная компрессия HDR Настройки Файл eu07.ini изменён вне стартера. Загрузить заново? Добавить всю категорию