Add a developer tab with the state of the vehicle database

This commit is contained in:
maj00r
2026-10-04 12:18:36 +02:00
parent 93463ae7f2
commit 9d981cb093
13 changed files with 289 additions and 2 deletions

View File

@@ -35,6 +35,7 @@ public sealed class SimulatorSettings
public bool DebugMode;
public bool VirtualShunting = true;
public bool LogMissingVehicleFiles;
public bool DeveloperTools;
public int RenderEngine;
public int Width = 1280;

View File

@@ -56,6 +56,7 @@ public sealed class SettingsSerializer
s.DebugMode = c.GetBool("debugmode", false);
s.VirtualShunting = c.GetBool("ai.trainman", true);
s.LogMissingVehicleFiles = c.GetBool("starter.logmissingvehicles", false);
s.DeveloperTools = c.GetBool("starter.developertools", false);
s.RenderEngine = IndexOf(SimulatorSettings.RenderEngines, c.GetString("gfxrenderer", "full"), 0);
s.Width = c.GetInt("width", 1280);
@@ -195,6 +196,7 @@ public sealed class SettingsSerializer
c.SetBool("debugmode", s.DebugMode);
c.SetBool("ai.trainman", s.VirtualShunting);
c.SetBool("starter.logmissingvehicles", s.LogMissingVehicleFiles);
c.SetBool("starter.developertools", s.DeveloperTools);
c.Set("gfxrenderer", SimulatorSettings.RenderEngines[Clamp(s.RenderEngine, 0, SimulatorSettings.RenderEngines.Length - 1)]);
c.SetInt("width", s.Width);

View File

@@ -37,6 +37,10 @@
Tag="settings"
IsCheckedChanged="Nav_OnCheckedChanged"
Content="{Binding [NavSettings], Source={x:Static starterNg:App.Loc}}" />
<RadioButton x:Name="NavDeveloper" GroupName="nav" Theme="{StaticResource NavTab}"
Tag="developer" IsVisible="False"
IsCheckedChanged="Nav_OnCheckedChanged"
Content="{Binding [NavDeveloper], Source={x:Static starterNg:App.Loc}}" />
<Button Classes="Basic" Click="openLastLogClick" Width="48" Cursor="Hand"
HorizontalContentAlignment="Center"
ToolTip.Tip="{Binding [ShowLatestLogTooltip], Source={x:Static starterNg:App.Loc}}">
@@ -93,6 +97,7 @@
<views:Scenarios x:Name="ScenariosView" />
<views:Depot x:Name="DepotView" IsVisible="False" />
<views:Settings x:Name="SettingsView" IsVisible="False" />
<views:Developer x:Name="DeveloperView" IsVisible="False" />
</Panel>
</DockPanel>
</Window>

View File

@@ -56,6 +56,8 @@ public partial class MainWindow : Window
startButton.AddHandler(PointerReleasedEvent, StartButton_OnRightClick,
RoutingStrategies.Tunnel);
SettingsView.DeveloperToolsChanged += ApplyDeveloperTab;
SettingsView.ThumbnailSizeChanged += () =>
{
DepotView.RefreshConsistView();
@@ -172,7 +174,7 @@ public partial class MainWindow : Window
if (sender is not RadioButton { IsChecked: true } rb)
return;
if (ScenariosView is null || DepotView is null || SettingsView is null)
if (ScenariosView is null || DepotView is null || SettingsView is null || DeveloperView is null)
return;
ShowPage(rb.Tag as string);
@@ -184,11 +186,31 @@ public partial class MainWindow : Window
ScenariosView.IsVisible = page == "scenarios";
DepotView.IsVisible = page == "depot";
SettingsView.IsVisible = page == "settings";
DeveloperView.IsVisible = page == "developer";
if (DeveloperView.IsVisible)
DeveloperView.Refresh();
}
/// <summary>
/// Shows or hides the developer tab. Switching it off while it is the open one
/// hands the window back to the scenarios, so the top bar never ends up with
/// nothing selected.
/// </summary>
private void ApplyDeveloperTab()
{
bool wanted = AppServices.Current.Settings.DeveloperTools;
NavDeveloper.IsVisible = wanted;
if (!wanted && NavDeveloper.IsChecked == true)
NavScenarios.IsChecked = true;
}
private void ApplyInitialNav()
{
foreach (var rb in new[] { NavScenarios, NavDepot, NavSettings })
ApplyDeveloperTab();
foreach (var rb in new[] { NavScenarios, NavDepot, NavSettings, NavDeveloper })
if (rb.IsChecked == true)
{
ShowPage(rb.Tag as string);

View File

@@ -0,0 +1,65 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:starterNg="clr-namespace:StarterNG"
x:CompileBindings="True"
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="700"
x:Class="StarterNG.Views.Developer">
<!-- Read-only view of what the starter made of the installation: the counts a
package author needs before slicing anything out of it, and the state of
the JSON copy those packages will be generated from. -->
<StackPanel Margin="3" Spacing="3">
<Border Background="{StaticResource CarbonPanelBg}" BorderBrush="{StaticResource CarbonBorderLight}"
BorderThickness="1" CornerRadius="2" Padding="10" HorizontalAlignment="Left" MinWidth="440">
<StackPanel Spacing="8">
<TextBlock Text="{Binding [DevDatabase], Source={x:Static starterNg:App.Loc}}"
FontWeight="Bold" FontSize="14" />
<Grid ColumnDefinitions="190,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"
RowSpacing="4">
<TextBlock Grid.Row="0" Grid.Column="0" Foreground="{StaticResource CarbonFgDim}"
Text="{Binding [DevVehicles], Source={x:Static starterNg:App.Loc}}" />
<TextBlock Grid.Row="0" Grid.Column="1" x:Name="VehiclesValue" />
<TextBlock Grid.Row="1" Grid.Column="0" Foreground="{StaticResource CarbonFgDim}"
Text="{Binding [DevLiveries], Source={x:Static starterNg:App.Loc}}" />
<TextBlock Grid.Row="1" Grid.Column="1" x:Name="LiveriesValue" />
<TextBlock Grid.Row="2" Grid.Column="0" Foreground="{StaticResource CarbonFgDim}"
Text="{Binding [DevGroups], Source={x:Static starterNg:App.Loc}}" />
<TextBlock Grid.Row="2" Grid.Column="1" x:Name="GroupsValue" />
<TextBlock Grid.Row="3" Grid.Column="0" Foreground="{StaticResource CarbonFgDim}"
Text="{Binding [DevSets], Source={x:Static starterNg:App.Loc}}" />
<TextBlock Grid.Row="3" Grid.Column="1" x:Name="SetsValue" />
<TextBlock Grid.Row="4" Grid.Column="0" Foreground="{StaticResource CarbonFgDim}"
Text="{Binding [DevFiles], Source={x:Static starterNg:App.Loc}}" />
<TextBlock Grid.Row="4" Grid.Column="1" x:Name="FilesValue" />
<TextBlock Grid.Row="5" Grid.Column="0" Foreground="{StaticResource CarbonFgDim}"
Text="{Binding [DevSource], Source={x:Static starterNg:App.Loc}}" />
<TextBlock Grid.Row="5" Grid.Column="1" x:Name="SourceValue" />
<TextBlock Grid.Row="6" Grid.Column="0" Foreground="{StaticResource CarbonFgDim}"
Text="{Binding [DevWritten], Source={x:Static starterNg:App.Loc}}" />
<TextBlock Grid.Row="6" Grid.Column="1" x:Name="WrittenValue" />
<TextBlock Grid.Row="7" Grid.Column="0" Foreground="{StaticResource CarbonFgDim}"
Text="{Binding [DevLocation], Source={x:Static starterNg:App.Loc}}"
VerticalAlignment="Top" />
<SelectableTextBlock Grid.Row="7" Grid.Column="1" x:Name="LocationValue"
TextWrapping="Wrap" FontSize="11" />
</Grid>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button x:Name="RebuildButton" Click="RebuildButton_OnClick" Cursor="Hand"
Content="{Binding [DevRebuild], Source={x:Static starterNg:App.Loc}}" />
<TextBlock x:Name="RebuildStatus" VerticalAlignment="Center"
Foreground="{StaticResource CarbonFgDim}" />
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,78 @@
using System;
using System.Globalization;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using StarterNG.Application;
using StarterNG.Domain.Vehicles;
namespace StarterNG.Views;
/// <summary>
/// The developer tab: what the starter read out of the installation, and the state
/// of the JSON copy it keeps of it. Read-only apart from rebuilding that copy.
/// </summary>
public partial class Developer : UserControl
{
public Developer()
{
InitializeComponent();
}
private void InitializeComponent() => AvaloniaXamlLoader.Load(this);
/// <summary>Fills the panel from the catalogue as it stands right now.</summary>
public void Refresh()
{
var catalog = AppServices.Current.Library.Vehicles;
var status = AppServices.Current.VehicleCache.Status;
VehiclesValue.Text = Count(catalog.TextureByUuid.Values
.Select(texture => texture.Directory)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Count());
LiveriesValue.Text = Wrecks(catalog);
GroupsValue.Text = Count(catalog.GroupsById.Count);
SetsValue.Text = Count(catalog.Sets.Count);
FilesValue.Text = Count(status.Sources);
SourceValue.Text = App.Loc[status.ServedFromCopy ? "DevSourceCopy" : "DevSourceTextures"];
WrittenValue.Text = status.Written is { } written
? written.ToLocalTime().ToString("yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture)
: "-";
LocationValue.Text = status.Directory;
RebuildStatus.Text = "";
}
/// <summary>
/// Rebuilds the copy on disk without disturbing the catalogue the other tabs
/// are already drawn from - the vehicles on screen keep pointing at the entries
/// they were built with.
/// </summary>
private void RebuildButton_OnClick(object? sender, RoutedEventArgs e)
{
RebuildButton.IsEnabled = false;
try
{
int liveries = AppServices.Current.VehicleCache
.Rebuild(new VehicleCatalog(AppServices.Current.MiniTextures));
Refresh();
RebuildStatus.Text = $"{App.Loc["DevRebuilt"]} ({liveries})";
}
finally
{
RebuildButton.IsEnabled = true;
}
}
/// <summary>Liveries offered, saying so when wrecks are being held back.</summary>
private static string Wrecks(VehicleCatalog catalog)
{
int wrecks = catalog.TextureByUuid.Count - catalog.Textures.Count;
return wrecks > 0
? $"{Count(catalog.Textures.Count)} (+{Count(wrecks)} {App.Loc["DevWrecks"]})"
: Count(catalog.Textures.Count);
}
private static string Count(int value) => value.ToString(CultureInfo.InvariantCulture);
}

View File

@@ -144,6 +144,9 @@
<ctl:SettingRow ConfigKey="starter.logmissingvehicles" Description="{Binding [LogMissingVehicleFilesDesc], Source={x:Static starterNg:App.Loc}}">
<CheckBox x:Name="LogMissingVehicleFilesCb" Content="{Binding [LogMissingVehicleFiles], Source={x:Static starterNg:App.Loc}}" />
</ctl:SettingRow>
<ctl:SettingRow ConfigKey="starter.developertools" Description="{Binding [DeveloperToolsDesc], Source={x:Static starterNg:App.Loc}}">
<CheckBox x:Name="DeveloperToolsCb" Content="{Binding [DeveloperTools], Source={x:Static starterNg:App.Loc}}" />
</ctl:SettingRow>
</StackPanel>
</ctl:SettingsSection>
</StackPanel>

View File

@@ -34,9 +34,15 @@ public partial class Settings : UserControl, ISettingsCapture
/// </summary>
public event Action? ThumbnailSizeChanged;
/// <summary>Raised when the developer tab is switched on or off, so the top bar can follow.</summary>
public event Action? DeveloperToolsChanged;
/// <summary>Thumbnail size the other tabs were last drawn at.</summary>
private bool _drawnThumbs = AppServices.Current.Settings.LargeThumbnails;
/// <summary>Whether the top bar was last drawn with the developer tab on it.</summary>
private bool _shownDeveloperTools = AppServices.Current.Settings.DeveloperTools;
public Settings()
{
InitializeComponent();
@@ -199,6 +205,7 @@ public partial class Settings : UserControl, ISettingsCapture
DebugModeCb.IsChecked = s.DebugMode;
VirtualShuntingCb.IsChecked = s.VirtualShunting;
LogMissingVehicleFilesCb.IsChecked = s.LogMissingVehicleFiles;
DeveloperToolsCb.IsChecked = s.DeveloperTools;
RenderEngineCb.SelectedIndex = s.RenderEngine;
SelectResolution(s.Width, s.Height);
@@ -297,6 +304,7 @@ public partial class Settings : UserControl, ISettingsCapture
s.DebugMode = IsChecked(DebugModeCb);
s.VirtualShunting = IsChecked(VirtualShuntingCb);
s.LogMissingVehicleFiles = IsChecked(LogMissingVehicleFilesCb);
s.DeveloperTools = IsChecked(DeveloperToolsCb);
s.RenderEngine = Math.Max(0, RenderEngineCb.SelectedIndex);
ReadResolution(s);
@@ -373,12 +381,23 @@ public partial class Settings : UserControl, ISettingsCapture
AppServices.Current.SettingsStore.Save();
KeyboardConfig.Instance.Save();
RedrawThumbnailsIfNeeded();
RedrawNavIfNeeded();
_dirty = false;
UpdateSaveState();
if (SaveStatus is not null)
SaveStatus.Text = App.Loc["SettingsSaved"];
}
private void RedrawNavIfNeeded()
{
bool developer = AppServices.Current.Settings.DeveloperTools;
if (developer == _shownDeveloperTools)
return;
_shownDeveloperTools = developer;
DeveloperToolsChanged?.Invoke();
}
private void RedrawThumbnailsIfNeeded()
{
bool large = AppServices.Current.Settings.LargeThumbnails;

View File

@@ -7,6 +7,7 @@
<String key="NavScenarios">Scénáře</String>
<String key="NavDepot">Depo</String>
<String key="NavSettings">Nastavení</String>
<String key="NavDeveloper">Developer</String>
<String key="NavControls">Ovládání</String>
<String key="Help">Nápověda</String>
<String key="HelpWiki">Wiki MaSzyna</String>
@@ -334,6 +335,7 @@
<String key="DebugMode">Režim ladění (testovací režim)</String>
<String key="VirtualShunting">Virtuální posun</String>
<String key="LogMissingVehicleFiles">Zaznamenat chybějící soubory vozidel při exportu</String>
<String key="DeveloperTools">Show the package author's tab</String>
<String key="Graphic">Grafika</String>
<String key="RenderEngine">Vykreslovací engine</String>
@@ -584,4 +586,20 @@
<String key="ShortcutMember">Vybrat člen ve spojené jednotce</String>
<String key="ShortcutMoveVehicle">Posunout vozidlo v soupravě</String>
<String key="ShortcutCardMenu">Otevřít nabídku vozidla</String>
<!-- Package author's tab - awaiting translation -->
<String key="DevDatabase">Vehicle database</String>
<String key="DevVehicles">Vehicle folders</String>
<String key="DevLiveries">Liveries</String>
<String key="DevWrecks">wrecks</String>
<String key="DevGroups">Groups</String>
<String key="DevSets">Fixed sets</String>
<String key="DevFiles">textures.txt files</String>
<String key="DevSource">Read from</String>
<String key="DevSourceCopy">the starter's copy</String>
<String key="DevSourceTextures">textures.txt files</String>
<String key="DevWritten">Copy written</String>
<String key="DevLocation">Copy folder</String>
<String key="DevRebuild">Rebuild copy</String>
<String key="DevRebuilt">Rebuilt</String>
</Language>

View File

@@ -7,6 +7,7 @@
<String key="NavScenarios">Scenarios</String>
<String key="NavDepot">Depot</String>
<String key="NavSettings">Settings</String>
<String key="NavDeveloper">Developer</String>
<String key="NavControls">Controls</String>
<String key="Help">Help</String>
<String key="HelpWiki">MaSzyna wiki</String>
@@ -340,6 +341,8 @@
<String key="VirtualShuntingDesc">Enables an AI assistant for coupling, uncoupling and simple shunting moves while the vehicle is stopped.</String>
<String key="LogMissingVehicleFiles">Log missing vehicle files during export</String>
<String key="LogMissingVehicleFilesDesc">Writes vehicles the starter could not find into the log.</String>
<String key="DeveloperTools">Show the package author's tab</String>
<String key="DeveloperToolsDesc">Adds a tab with the state of the vehicle database, for preparing rolling stock packages.</String>
<String key="Graphic">Graphics</String>
<String key="RenderEngine">Render engine</String>
@@ -619,4 +622,20 @@
<String key="ShortcutMember">Pick a car inside a coupled unit</String>
<String key="ShortcutMoveVehicle">Move the vehicle along the consist</String>
<String key="ShortcutCardMenu">Open the vehicle menu</String>
<!-- Package author's tab -->
<String key="DevDatabase">Vehicle database</String>
<String key="DevVehicles">Vehicle folders</String>
<String key="DevLiveries">Liveries</String>
<String key="DevWrecks">wrecks</String>
<String key="DevGroups">Groups</String>
<String key="DevSets">Fixed sets</String>
<String key="DevFiles">textures.txt files</String>
<String key="DevSource">Read from</String>
<String key="DevSourceCopy">the starter's copy</String>
<String key="DevSourceTextures">textures.txt files</String>
<String key="DevWritten">Copy written</String>
<String key="DevLocation">Copy folder</String>
<String key="DevRebuild">Rebuild copy</String>
<String key="DevRebuilt">Rebuilt</String>
</Language>

View File

@@ -7,6 +7,7 @@
<String key="NavScenarios">Forgatókönyvek</String>
<String key="NavDepot">Fűtőház</String>
<String key="NavSettings">Beállítások</String>
<String key="NavDeveloper">Developer</String>
<String key="NavControls">Irányítás</String>
<String key="Help">Súgó</String>
<String key="HelpWiki">MaSzyna wiki</String>
@@ -334,6 +335,7 @@
<String key="DebugMode">Hibakeresési mód (teszt mód)</String>
<String key="VirtualShunting">Virtuális tolatás</String>
<String key="LogMissingVehicleFiles">Hiányzó járműfájlok naplózása exportáláskor</String>
<String key="DeveloperTools">Show the package author's tab</String>
<String key="Graphic">Grafika</String>
<String key="RenderEngine">Megjelenítő motor</String>
@@ -584,4 +586,20 @@
<String key="ShortcutMember">Tag kiválasztása kapcsolt egységen belül</String>
<String key="ShortcutMoveVehicle">Jármű mozgatása a szerelvényben</String>
<String key="ShortcutCardMenu">Jármű menüjének megnyitása</String>
<!-- Package author's tab - awaiting translation -->
<String key="DevDatabase">Vehicle database</String>
<String key="DevVehicles">Vehicle folders</String>
<String key="DevLiveries">Liveries</String>
<String key="DevWrecks">wrecks</String>
<String key="DevGroups">Groups</String>
<String key="DevSets">Fixed sets</String>
<String key="DevFiles">textures.txt files</String>
<String key="DevSource">Read from</String>
<String key="DevSourceCopy">the starter's copy</String>
<String key="DevSourceTextures">textures.txt files</String>
<String key="DevWritten">Copy written</String>
<String key="DevLocation">Copy folder</String>
<String key="DevRebuild">Rebuild copy</String>
<String key="DevRebuilt">Rebuilt</String>
</Language>

View File

@@ -7,6 +7,7 @@
<String key="NavScenarios">Scenariusze</String>
<String key="NavDepot">Zajezdnia</String>
<String key="NavSettings">Ustawienia</String>
<String key="NavDeveloper">Deweloper</String>
<String key="NavControls">Sterowanie</String>
<String key="Help">Pomoc</String>
<String key="HelpWiki">Wiki MaSzyna</String>
@@ -340,6 +341,8 @@
<String key="VirtualShuntingDesc">Włącza asystenta AI do sprzęgania, rozprzęgania i prostych manewrów, gdy pojazd stoi.</String>
<String key="LogMissingVehicleFiles">Zapisuj w logu brakujące pliki pojazdów podczas eksportu</String>
<String key="LogMissingVehicleFilesDesc">Zapisuje w logu pojazdy, których starter nie znalazł.</String>
<String key="DeveloperTools">Pokaż zakładkę dla twórców paczek</String>
<String key="DeveloperToolsDesc">Dodaje zakładkę ze stanem bazy pojazdów, przydatną przy przygotowywaniu paczek z taborem.</String>
<String key="Graphic">Grafika</String>
<String key="RenderEngine">Silnik renderowania</String>
@@ -619,4 +622,20 @@
<String key="ShortcutMember">Wybierz człon w zespolonej jednostce</String>
<String key="ShortcutMoveVehicle">Przesuń pojazd w składzie</String>
<String key="ShortcutCardMenu">Otwórz menu pojazdu</String>
<!-- Zakladka dla tworcow paczek -->
<String key="DevDatabase">Baza pojazdów</String>
<String key="DevVehicles">Katalogi pojazdów</String>
<String key="DevLiveries">Malowania</String>
<String key="DevWrecks">wraki</String>
<String key="DevGroups">Grupy</String>
<String key="DevSets">Zespoły stałe</String>
<String key="DevFiles">Pliki textures.txt</String>
<String key="DevSource">Wczytano z</String>
<String key="DevSourceCopy">kopii startera</String>
<String key="DevSourceTextures">plików textures.txt</String>
<String key="DevWritten">Kopia zapisana</String>
<String key="DevLocation">Katalog kopii</String>
<String key="DevRebuild">Odbuduj kopię</String>
<String key="DevRebuilt">Odbudowano</String>
</Language>

View File

@@ -7,6 +7,7 @@
<String key="NavScenarios">Сценарии</String>
<String key="NavDepot">Депо</String>
<String key="NavSettings">Настройки</String>
<String key="NavDeveloper">Developer</String>
<String key="NavControls">Управление</String>
<String key="Help">Справка</String>
<String key="HelpWiki">Вики MaSzyna</String>
@@ -334,6 +335,7 @@
<String key="DebugMode">Режим отладки (тестовый режим)</String>
<String key="VirtualShunting">Виртуальный манёвр</String>
<String key="LogMissingVehicleFiles">Записывать в журнал отсутствующие файлы состава при экспорте</String>
<String key="DeveloperTools">Show the package author's tab</String>
<String key="Graphic">Графика</String>
<String key="RenderEngine">Движок отрисовки</String>
@@ -584,4 +586,20 @@
<String key="ShortcutMember">Выбрать секцию в сцепленной единице</String>
<String key="ShortcutMoveVehicle">Переместить вагон в составе</String>
<String key="ShortcutCardMenu">Открыть меню вагона</String>
<!-- Package author's tab - awaiting translation -->
<String key="DevDatabase">Vehicle database</String>
<String key="DevVehicles">Vehicle folders</String>
<String key="DevLiveries">Liveries</String>
<String key="DevWrecks">wrecks</String>
<String key="DevGroups">Groups</String>
<String key="DevSets">Fixed sets</String>
<String key="DevFiles">textures.txt files</String>
<String key="DevSource">Read from</String>
<String key="DevSourceCopy">the starter's copy</String>
<String key="DevSourceTextures">textures.txt files</String>
<String key="DevWritten">Copy written</String>
<String key="DevLocation">Copy folder</String>
<String key="DevRebuild">Rebuild copy</String>
<String key="DevRebuilt">Rebuilt</String>
</Language>