diff --git a/StarterNG/Views/Depot.axaml b/StarterNG/Views/Depot.axaml
index 2990ac0..0c92ce4 100644
--- a/StarterNG/Views/Depot.axaml
+++ b/StarterNG/Views/Depot.axaml
@@ -30,55 +30,61 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
+ SelectionChanged="CategoryCombo_OnSelectionChanged" />
+
-
-
-
+
-
+
-
+
+
+
+
+
+
+
+
diff --git a/StarterNG/Views/Depot/VehicleBrowser.cs b/StarterNG/Views/Depot/VehicleBrowser.cs
index da55b61..b699225 100644
--- a/StarterNG/Views/Depot/VehicleBrowser.cs
+++ b/StarterNG/Views/Depot/VehicleBrowser.cs
@@ -94,24 +94,15 @@ public sealed class VehicleBrowser
private const int MiniPreviewHeight = 54;
- private const int CompactThumbHeight = 24;
+ // In the old starter's order: traction first, then the wagons, then the rest.
private static readonly (string LocKey, Func Match)[] CategoryDefs =
{
("CatElectricLoco", c => c == "e"),
("CatDieselLoco", c => c == "s"),
("CatSteamLoco", c => c == "p"),
- ("CatEMU", c => c == "z"),
("CatRailbus", c => c == "a"),
- ("CatDraisine", c => c == "d"),
- ("CatWork", c => c == "r"),
- ("CatPrototype", c => c == "x"),
- ("CatTram", c => c == "t"),
- ("CatCar", c => c == "o"),
- ("CatBus", c => c == "b"),
- ("CatTruck", c => c == "c"),
- ("CatPeople", c => c == "h"),
- ("CatAnimals", c => c == "f"),
+ ("CatEMU", c => c == "z"),
("CatWagonsA", c => c == "A"),
("CatWagonsB", c => c == "B"),
("CatWagonsC", c => c == "C"),
@@ -137,6 +128,15 @@ public sealed class VehicleBrowser
("CatWagonsX", c => c == "X"),
("CatWagonsY", c => c == "Y"),
("CatWagonsZ", c => c == "Z"),
+ ("CatWork", c => c == "r"),
+ ("CatDraisine", c => c == "d"),
+ ("CatTram", c => c == "t"),
+ ("CatCar", c => c == "o"),
+ ("CatBus", c => c == "b"),
+ ("CatTruck", c => c == "c"),
+ ("CatPeople", c => c == "h"),
+ ("CatAnimals", c => c == "f"),
+ ("CatPrototype", c => c == "x"),
("CatOther", IsOtherCat),
};
@@ -148,47 +148,76 @@ public sealed class VehicleBrowser
string.IsNullOrEmpty(c) ||
(c is { Length: 1 } && char.IsLower(c[0]) && !NamedLowerCats.Contains(c));
+ ///
+ /// Only the categories something is listed in - with the archival ones hidden, the
+ /// ones holding nothing else go too - keeping the pick and its class where they stay.
+ ///
public void PopulateCategoryCombo()
{
- int keep = categoryCombo.SelectedIndex;
+ var keep = (categoryCombo.SelectedItem as ComboBoxItem)?.Tag;
+ string? keepClass = _classFilter;
+
+ var present = _db.Textures
+ .Where(Listable)
+ .Select(VehicleInfo.CategoryOf)
+ .Distinct()
+ .ToList();
_suppress = true;
categoryCombo.Items.Clear();
foreach (var (locKey, match) in CategoryDefs)
- categoryCombo.Items.Add(new ComboBoxItem { Content = App.Loc[locKey], Tag = match });
- categoryCombo.SelectedIndex = keep >= 0 && keep < categoryCombo.Items.Count ? keep : -1;
+ if (present.Any(match))
+ categoryCombo.Items.Add(new ComboBoxItem { Content = App.Loc[locKey], Tag = match });
+ categoryCombo.SelectedItem = categoryCombo.Items.OfType()
+ .FirstOrDefault(item => ReferenceEquals(item.Tag, keep));
_suppress = false;
_categoryFilter = categoryCombo.SelectedItem is ComboBoxItem { Tag: Func f }
? f : null;
- RebuildClassCombo();
- }
-
- private void RebuildClassCombo()
- {
- _suppress = true;
- FillClassCombo();
- classCombo.SelectedIndex = -1;
- _suppress = false;
-
- _classFilter = null;
- Rebuild();
+ RebuildClassCombo(keepClass);
}
///
- /// The open list shows its thumbnails at the size the consist cards use, large or
- /// small as set; the closed box keeps a compact line. Called again when that
- /// setting changes, so the open list follows.
+ /// Refills the classes. Within a category one is always picked - the one kept, or
+ /// else the first - so the list shows its vehicles straight away, as the old starter
+ /// did, instead of every vehicle of the category at once.
+ ///
+ private void RebuildClassCombo(string? keepClass = null)
+ {
+ _suppress = true;
+ FillClassCombo();
+ var classes = classCombo.Items.OfType().ToList();
+ classCombo.SelectedItem =
+ classes.FirstOrDefault(cls => string.Equals(cls, keepClass, StringComparison.OrdinalIgnoreCase))
+ ?? (_categoryFilter is not null ? classes.FirstOrDefault() : null);
+ _suppress = false;
+
+ _classFilter = classCombo.SelectedItem as string;
+ Rebuild();
+ }
+
+ /// A texture the browser lists at all: no set follower, no hidden archival one.
+ private bool Listable(VehicleTexture texture) =>
+ !_db.IsSetFollower(texture) &&
+ !(AppServices.Current.Settings.HideArchivalVehicles && texture.ResolvedArchived);
+
+ ///
+ /// The classes with their thumbnail above the name: in the open list at the size
+ /// the consist cards use, called again when that size changes; in the closed box
+ /// at a fixed modest one, so the class picked is seen, as in the old starter,
+ /// without taking the height the vehicle list needs.
///
public void InitClassComboTemplates()
{
- int listHeight = VehicleCardStyle.ThumbHeight;
+ int height = VehicleCardStyle.ThumbHeight;
classCombo.ItemTemplate = new FuncDataTemplate(
- (cls, _) => ClassComboContent(cls, listHeight, stacked: true), false);
+ (cls, _) => ClassComboContent(cls, height, stacked: true), false);
classCombo.SelectionBoxItemTemplate = new FuncDataTemplate(
- (cls, _) => ClassComboContent(cls, CompactThumbHeight, stacked: false), false);
+ (cls, _) => ClassComboContent(cls, PickedClassThumbHeight, stacked: true), false);
}
+ private const int PickedClassThumbHeight = 30;
+
private void FillClassCombo()
{
classCombo.Items.Clear();
@@ -257,9 +286,8 @@ public sealed class VehicleBrowser
private IEnumerable ClassesForCategory(Func? category) =>
_db.Textures
+ .Where(Listable)
.Where(t => category == null || category(VehicleInfo.CategoryOf(t)))
- .Where(t => !_db.IsSetFollower(t))
- .Where(t => !AppServices.Current.Settings.HideArchivalVehicles || !t.ResolvedArchived)
.Select(VehicleInfo.ClassOf)
.Where(s => !string.IsNullOrEmpty(s))
.Distinct(StringComparer.OrdinalIgnoreCase)
@@ -319,7 +347,10 @@ public sealed class VehicleBrowser
bool hasSearch = search.Length > 0;
if (_categoryFilter == null && _classFilter == null && !hasSearch)
+ {
+ AddNote(App.Loc["BrowserHint"]);
return;
+ }
var matched = _db.Textures
.Where(t => PassesFilters(t, search, hasSearch))
@@ -338,13 +369,16 @@ public sealed class VehicleBrowser
}
if (vehicleListBox.Items.Count == 0)
- vehicleListBox.Items.Add(new ListBoxItem
- {
- Content = App.Loc["NoVehicles"],
- IsEnabled = false
- });
+ AddNote(hasSearch ? string.Format(App.Loc["BrowserNoMatch"], search) : App.Loc["NoVehicles"]);
}
+ private void AddNote(string text) =>
+ vehicleListBox.Items.Add(new ListBoxItem
+ {
+ Content = new TextBlock { Text = text, TextWrapping = TextWrapping.Wrap, Opacity = 0.7 },
+ IsEnabled = false
+ });
+
public void VehicleListBox_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (vehicleListBox.SelectedItem is ListBoxItem { Tag: VehicleTexture texture })
@@ -489,23 +523,19 @@ public sealed class VehicleBrowser
private bool PassesFilters(VehicleTexture t, string search, bool hasSearch)
{
+ if (!Listable(t))
+ return false;
+
+ // A search looks through everything; the category and the class narrow the
+ // browsing, not the finding.
+ if (hasSearch)
+ return Matches(t, search);
+
if (_categoryFilter != null && !_categoryFilter(VehicleInfo.CategoryOf(t)))
return false;
- if (_classFilter != null &&
- !string.Equals(VehicleInfo.ClassOf(t), _classFilter, StringComparison.OrdinalIgnoreCase))
- return false;
-
- if (_db.IsSetFollower(t))
- return false;
-
- if (AppServices.Current.Settings.HideArchivalVehicles && t.ResolvedArchived)
- return false;
-
- if (hasSearch && !Matches(t, search))
- return false;
-
- return true;
+ return _classFilter == null ||
+ string.Equals(VehicleInfo.ClassOf(t), _classFilter, StringComparison.OrdinalIgnoreCase);
}
private bool Matches(VehicleTexture t, string f)
@@ -527,15 +557,20 @@ public sealed class VehicleBrowser
// called a vehicle something none of the other panels did.
private static string BrowserName(VehicleTexture texture) => Consist.Base(texture.Skinfile);
- private string BrowserLabel(VehicleTexture texture, IReadOnlyList? set)
+ // The file name alone, as the old starter listed it - the operator is in the
+ // texture panel - with a faint count for a set of cars.
+ private static Control BrowserLabel(VehicleTexture texture, IReadOnlyList? set)
{
- string name = BrowserName(texture);
+ var name = new TextBlock { Text = BrowserName(texture) };
+ if (set is not { Count: > 1 })
+ return name;
- if (!string.IsNullOrEmpty(texture.Meta?.Operator))
- name += $" · {texture.Meta!.Operator}";
- if (set is { Count: > 1 })
- name += $" [{set.Count}]";
- return name;
+ return new StackPanel
+ {
+ Orientation = Orientation.Horizontal,
+ Spacing = 6,
+ Children = { name, new TextBlock { Text = $"×{set.Count}", Opacity = 0.55 } }
+ };
}
public void SearchBox_OnTextChanged(object? sender, TextChangedEventArgs e)
@@ -550,7 +585,7 @@ public sealed class VehicleBrowser
if (_suppress) return;
AppServices.Current.Settings.HideArchivalVehicles = hideArchivalCheck.IsChecked ?? true;
AppServices.Current.SettingsStore.Save();
- Rebuild();
+ PopulateCategoryCombo();
}
public void SelectInBrowser(Dynamic car)
diff --git a/StarterNG/startercfg/lang/cz.xml b/StarterNG/startercfg/lang/cz.xml
index c6e2507..0064dc5 100644
--- a/StarterNG/startercfg/lang/cz.xml
+++ b/StarterNG/startercfg/lang/cz.xml
@@ -95,6 +95,9 @@
s osádkouotočenoVe složce dynamic/**/textures.txt nebyla nalezena žádná vozidla.
+ Vyberte kategorii nebo napište název vozidla.
+ Nic neodpovídá „{0}“.
+ Hledat ve všech vozidlech…Přidejte vozidla z databáze vlevo nebo načtěte existující soupravu ze souboru scenérie.Posunout dolevaPosunout doprava
diff --git a/StarterNG/startercfg/lang/en.xml b/StarterNG/startercfg/lang/en.xml
index 41df810..0c60b01 100644
--- a/StarterNG/startercfg/lang/en.xml
+++ b/StarterNG/startercfg/lang/en.xml
@@ -95,6 +95,9 @@
with crewreversedNo vehicles found in dynamic/**/textures.txt.
+ Pick a category or type a vehicle's name.
+ Nothing matches “{0}”.
+ Search all vehicles…Add vehicles from the database on the left, or load an existing consist from a scenery file.Move leftMove right
diff --git a/StarterNG/startercfg/lang/hu.xml b/StarterNG/startercfg/lang/hu.xml
index fead075..721329b 100644
--- a/StarterNG/startercfg/lang/hu.xml
+++ b/StarterNG/startercfg/lang/hu.xml
@@ -95,6 +95,9 @@
személyzettelmegfordítvaA dynamic/**/textures.txt fájlokban nem található jármű.
+ Válassz kategóriát, vagy írd be a jármű nevét.
+ Semmi sem felel meg ennek: „{0}”.
+ Keresés az összes járműben…Adjon hozzá járműveket a bal oldali adatbázisból, vagy töltsön be meglévő szerelvényt egy pályafájlból.Mozgatás balraMozgatás jobbra
diff --git a/StarterNG/startercfg/lang/pl.xml b/StarterNG/startercfg/lang/pl.xml
index 5f66e24..90c13a3 100644
--- a/StarterNG/startercfg/lang/pl.xml
+++ b/StarterNG/startercfg/lang/pl.xml
@@ -95,6 +95,9 @@
z obsadąObróconyNie znaleziono pojazdów w dynamic/**/textures.txt.
+ Wybierz kategorię albo wpisz nazwę pojazdu.
+ Nic nie pasuje do „{0}”.
+ Szukaj we wszystkich pojazdach…Dodaj pojazdy z bazy po lewej stronie lub wczytaj istniejący skład z pliku scenerii.Przesuń w lewoPrzesuń w prawo
diff --git a/StarterNG/startercfg/lang/ru.xml b/StarterNG/startercfg/lang/ru.xml
index 8398f72..d557dcb 100644
--- a/StarterNG/startercfg/lang/ru.xml
+++ b/StarterNG/startercfg/lang/ru.xml
@@ -95,6 +95,9 @@
с бригадойразвёрнутВ dynamic/**/textures.txt подвижной состав не найден.
+ Выберите категорию или введите название единицы.
+ Ничего не найдено по запросу «{0}».
+ Искать во всех единицах…Добавьте единицы из базы слева или загрузите готовый состав из файла сценерии.Сдвинуть влевоСдвинуть вправо