Fix loads, some UI improvements

This commit is contained in:
2026-06-24 10:08:17 +02:00
parent 60f61a122d
commit 5908200bc4
8 changed files with 207 additions and 48 deletions

View File

@@ -29,13 +29,25 @@
<!-- Row 0: title / caption strip. Transparent background makes the <!-- Row 0: title / caption strip. Transparent background makes the
whole strip a drag handle (PointerPressed -> BeginMoveDrag), whole strip a drag handle (PointerPressed -> BeginMoveDrag),
and the OS buttons render at its top-right. --> and the OS buttons render at its top-right. The log button sits
<Border Grid.Row="0" Background="Transparent" on top of the strip, just left of the OS min/max/close buttons. -->
PointerPressed="TitleBar_OnPointerPressed"> <StackPanel Orientation="Horizontal" Spacing="16" Grid.Row="0">
<TextBlock Text="Starter MaSzyna" VerticalAlignment="Center" <Border Background="Transparent"
Margin="14,0,0,0" FontSize="11" PointerPressed="TitleBar_OnPointerPressed">
Foreground="{StaticResource CarbonFgDim}" /> <TextBlock Text="Starter MaSzyna" VerticalAlignment="Center"
</Border> Margin="14,0,0,0" FontSize="11"
Foreground="{StaticResource CarbonFgDim}" />
</Border>
<!-- Reserve ~140px on the right for the OS caption buttons so the
log button lands immediately to their left. -->
<Button Classes="Basic" Click="openLastLogClick"
HorizontalAlignment="Left" VerticalAlignment="Stretch"
Margin="0,0,0,0" Padding="10,0" Cursor="Hand"
ToolTip.Tip="{Binding [ShowLatestLogTooltip], Source={x:Static starterNg:App.Loc}}">
<materialIcons:MaterialIcon Kind="FileDocument" Height="16" Width="16" />
</Button>
</StackPanel>
<!-- Row 1: logo + navigation (left) and the right cluster (right) --> <!-- Row 1: logo + navigation (left) and the right cluster (right) -->
<Grid Grid.Row="1" ColumnDefinitions="Auto,Auto,*,Auto"> <Grid Grid.Row="1" ColumnDefinitions="Auto,Auto,*,Auto">
@@ -65,7 +77,7 @@
HorizontalContentAlignment="Center" /> HorizontalContentAlignment="Center" />
</StackPanel> </StackPanel>
<!-- Right cluster: version, language, log, social links. <!-- Right cluster: version, language, social links.
In the toolbar row, so it sits below the OS caption buttons. --> In the toolbar row, so it sits below the OS caption buttons. -->
<StackPanel Grid.Column="3" Orientation="Horizontal" VerticalAlignment="Center" <StackPanel Grid.Column="3" Orientation="Horizontal" VerticalAlignment="Center"
Margin="0,0,8,0" Spacing="4"> Margin="0,0,8,0" Spacing="4">
@@ -82,10 +94,6 @@
<ComboBoxItem>English</ComboBoxItem> <ComboBoxItem>English</ComboBoxItem>
</ComboBox> </ComboBox>
<Button Classes="Basic" Click="openLastLogClick"
ToolTip.Tip="{Binding [ShowLatestLogTooltip], Source={x:Static starterNg:App.Loc}}">
<materialIcons:MaterialIcon Kind="FileDocument" />
</Button>
<Button Classes="Basic" Cursor="Hand" Tag="http://eu07.pl/" Click="linkClick"> <Button Classes="Basic" Cursor="Hand" Tag="http://eu07.pl/" Click="linkClick">
<materialIcons:MaterialIcon Kind="Web" /> <materialIcons:MaterialIcon Kind="Web" />
</Button> </Button>

View File

@@ -181,8 +181,12 @@
</Style> </Style>
<!-- ── Compact list (tight rows that fill the available space) ─────────── --> <!-- ── Compact list (tight rows that fill the available space) ─────────── -->
<!-- Transparent surface so the list blends into the GroupBox panel instead
of showing the Fluent theme's default light-grey ListBox background. -->
<Style Selector="ListBox.Compact"> <Style Selector="ListBox.Compact">
<Setter Property="Padding" Value="0" /> <Setter Property="Padding" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
</Style> </Style>
<Style Selector="ListBox.Compact ListBoxItem"> <Style Selector="ListBox.Compact ListBoxItem">
<Setter Property="Padding" Value="6,2" /> <Setter Property="Padding" Value="6,2" />

View File

@@ -27,9 +27,11 @@
<Grid DockPanel.Dock="Top" ColumnDefinitions="*,*" ColumnSpacing="6" Margin="0,0,0,6"> <Grid DockPanel.Dock="Top" ColumnDefinitions="*,*" ColumnSpacing="6" Margin="0,0,0,6">
<ComboBox Name="categoryCombo" Grid.Column="0" HorizontalAlignment="Stretch" <ComboBox Name="categoryCombo" Grid.Column="0" HorizontalAlignment="Stretch"
PlaceholderText="{Binding [Category], Source={x:Static starterNg:App.Loc}}" PlaceholderText="{Binding [Category], Source={x:Static starterNg:App.Loc}}"
PointerWheelChanged="Combo_OnPointerWheel"
SelectionChanged="CategoryCombo_OnSelectionChanged" /> SelectionChanged="CategoryCombo_OnSelectionChanged" />
<ComboBox Name="classCombo" Grid.Column="1" HorizontalAlignment="Stretch" <ComboBox Name="classCombo" Grid.Column="1" HorizontalAlignment="Stretch"
PlaceholderText="{Binding [Class], Source={x:Static starterNg:App.Loc}}" PlaceholderText="{Binding [Class], Source={x:Static starterNg:App.Loc}}"
PointerWheelChanged="Combo_OnPointerWheel"
SelectionChanged="ClassCombo_OnSelectionChanged" /> SelectionChanged="ClassCombo_OnSelectionChanged" />
</Grid> </Grid>
<TextBox Name="searchBox" DockPanel.Dock="Top" Margin="0,0,0,8" <TextBox Name="searchBox" DockPanel.Dock="Top" Margin="0,0,0,8"

View File

@@ -337,6 +337,30 @@ public partial class Depot : UserControl
BuildList(); BuildList();
} }
// Hovering over the Type/Class combos and scrolling cycles the selection
// without having to open the dropdown (scroll up = previous, down = next).
// When the dropdown is open the wheel scrolls the list as usual.
private void Combo_OnPointerWheel(object? sender, PointerWheelEventArgs e)
{
if (sender is not ComboBox combo || combo.IsDropDownOpen)
return;
int count = combo.ItemCount;
if (count == 0)
return;
int index = combo.SelectedIndex;
if (e.Delta.Y > 0) // scroll up -> previous
index = index <= 0 ? 0 : index - 1;
else if (e.Delta.Y < 0) // scroll down -> next
index = index < 0 ? 0 : Math.Min(index + 1, count - 1);
else
return;
combo.SelectedIndex = index;
e.Handled = true;
}
// ----------------------------------------------------------- vehicle browser // ----------------------------------------------------------- vehicle browser
// Flat list of plain-text vehicle entries (no expanders, like the original // Flat list of plain-text vehicle entries (no expanders, like the original
@@ -1270,20 +1294,33 @@ public partial class Depot : UserControl
var phys = PhysicsFor(lead); var phys = PhysicsFor(lead);
// Prefer the cargo this vehicle actually accepts (.fiz LoadAccepted); // Prefer the cargo this vehicle actually accepts (.fiz LoadAccepted);
// otherwise suggest everything from data/load_weights.txt. Free text is // otherwise offer everything from data/load_weights.txt.
// still allowed. var available = phys != null && !string.IsNullOrWhiteSpace(phys.LoadAccepted)
var accepted = phys != null && !string.IsNullOrWhiteSpace(phys.LoadAccepted)
? phys.LoadAccepted.Split(',', ';').Select(s => s.Trim()).Where(s => s.Length > 0).ToList() ? phys.LoadAccepted.Split(',', ';').Select(s => s.Trim()).Where(s => s.Length > 0).ToList()
: null; : LoadTypes().ToList();
var typeBox = new AutoCompleteBox // Keep a load already set on the vehicle selectable even when it is not in
{ // the accepted/known list (e.g. a custom load read from the scenery).
FontSize = 12, if (!string.IsNullOrEmpty(lead.LoadType) &&
MinWidth = 200, !available.Contains(lead.LoadType!, StringComparer.OrdinalIgnoreCase))
Text = lead.LoadType ?? "", available.Insert(0, lead.LoadType!);
ItemsSource = accepted ?? (IEnumerable<string>)LoadTypes(),
FilterMode = AutoCompleteFilterMode.ContainsOrdinal // Dropdown of available cargo: the leading "(none)" entry clears the load.
}; var typeCombo = new ComboBox { FontSize = 12, MinWidth = 200 };
typeCombo.Items.Add(new ComboBoxItem { Content = App.Loc["None"], Tag = null });
foreach (var name in available)
typeCombo.Items.Add(new ComboBoxItem { Content = name, Tag = name });
// Pre-select the vehicle's current load (or "(none)").
typeCombo.SelectedIndex = 0;
if (!string.IsNullOrEmpty(lead.LoadType))
foreach (var obj in typeCombo.Items)
if (obj is ComboBoxItem ci && ci.Tag is string t &&
string.Equals(t, lead.LoadType, StringComparison.OrdinalIgnoreCase))
{
typeCombo.SelectedItem = ci;
break;
}
// Cap the amount at the .fiz MaxLoad when it is defined. // Cap the amount at the .fiz MaxLoad when it is defined.
int maxLoad = phys != null && phys.MaxLoad > 0 ? phys.MaxLoad : 1000; int maxLoad = phys != null && phys.MaxLoad > 0 ? phys.MaxLoad : 1000;
@@ -1295,22 +1332,25 @@ public partial class Depot : UserControl
void Apply() void Apply()
{ {
string type = typeBox.Text?.Trim() ?? ""; string? type = (typeCombo.SelectedItem as ComboBoxItem)?.Tag as string;
int count = (int)(countBox.Value ?? 0); int count = (int)(countBox.Value ?? 0);
foreach (var c in item.Cars) foreach (var c in item.Cars)
{ {
c.LoadCount = count; // "(none)" -> no cargo at all; otherwise store the chosen type/amount.
c.LoadType = count > 0 && !string.IsNullOrEmpty(type) ? type : c.LoadType; c.LoadType = string.IsNullOrEmpty(type) ? null : type;
if (count > 0) c.LoadCount = string.IsNullOrEmpty(type) ? 0 : count;
if (c.LoadCount > 0)
c.HasVelocity = true; // velocity token must precede loadcount c.HasVelocity = true; // velocity token must precede loadcount
} }
} }
typeBox.LostFocus += (_, _) => Apply(); // Wire the handlers only after the initial selection so we don't apply
// (and mark the consist dirty) while merely populating the editor.
typeCombo.SelectionChanged += (_, _) => Apply();
countBox.ValueChanged += (_, _) => Apply(); countBox.ValueChanged += (_, _) => Apply();
loadsPanel.Children.Add(UnitTitle(item)); loadsPanel.Children.Add(UnitTitle(item));
loadsPanel.Children.Add(LabeledRow(App.Loc["LoadType"], typeBox)); loadsPanel.Children.Add(LabeledRow(App.Loc["LoadType"], typeCombo));
loadsPanel.Children.Add(LabeledRow(App.Loc["LoadCount"], countBox)); loadsPanel.Children.Add(LabeledRow(App.Loc["LoadCount"], countBox));
loadsPanel.Children.Add(new TextBlock loadsPanel.Children.Add(new TextBlock
{ {

View File

@@ -59,9 +59,15 @@
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"
Text="{Binding [Time], Source={x:Static starterNg:App.Loc}}" /> Text="{Binding [Time], Source={x:Static starterNg:App.Loc}}" />
<TextBox Grid.Row="0" Grid.Column="1" Name="weatherTime" Width="90" <StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Spacing="8">
HorizontalAlignment="Left" Watermark="hh:mm" <TimePicker Name="weatherTime" ClockIdentifier="24HourClock"
LostFocus="Weather_OnChanged" /> MinuteIncrement="1" VerticalAlignment="Center"
SelectedTimeChanged="WeatherTime_OnChanged" />
<Button Name="weatherTimeNow" Classes="Flat" Cursor="Hand"
VerticalAlignment="Center" Padding="10,4"
Click="WeatherTimeNow_OnClick"
Content="{Binding [CurrentTime], Source={x:Static starterNg:App.Loc}}" />
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"
Text="{Binding [Season], Source={x:Static starterNg:App.Loc}}" /> Text="{Binding [Season], Source={x:Static starterNg:App.Loc}}" />
@@ -94,8 +100,8 @@
<TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" <TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center"
Text="{Binding [Visibility], Source={x:Static starterNg:App.Loc}}" /> Text="{Binding [Visibility], Source={x:Static starterNg:App.Loc}}" />
<StackPanel Grid.Row="4" Grid.Column="1" Orientation="Horizontal" Spacing="8"> <StackPanel Grid.Row="4" Grid.Column="1" Orientation="Horizontal" Spacing="8">
<Slider Name="weatherFog" Minimum="50" Maximum="2500" Width="220" <Slider Name="weatherFog" Minimum="0" Maximum="10000" Width="220"
TickFrequency="50" IsSnapToTickEnabled="True" TickFrequency="250" IsSnapToTickEnabled="True"
ValueChanged="Weather_OnSliderChanged" /> ValueChanged="Weather_OnSliderChanged" />
<TextBlock Name="weatherFogLabel" VerticalAlignment="Center" MinWidth="56" /> <TextBlock Name="weatherFogLabel" VerticalAlignment="Center" MinWidth="56" />
</StackPanel> </StackPanel>

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@@ -21,6 +23,9 @@ public partial class Scenarios : UserControl
private Scenery? _currentScenery; private Scenery? _currentScenery;
// suppresses weather change handlers while the form is being populated // suppresses weather change handlers while the form is being populated
private bool _loadingWeather; private bool _loadingWeather;
// "Today" season: the day field only previews the current day-of-year; the
// stored Day stays 0 so the scenery keeps using the live date.
private bool _todaySeason;
public Scenarios() public Scenarios()
{ {
@@ -30,6 +35,7 @@ public partial class Scenarios : UserControl
Sceneries = GameData.Instance.Sceneries; Sceneries = GameData.Instance.Sceneries;
var groupNodes = new Dictionary<string, TreeViewItem>(); var groupNodes = new Dictionary<string, TreeViewItem>();
var topLevel = new List<TreeViewItem>();
for (int i = 0; i < Sceneries.Count; i++) for (int i = 0; i < Sceneries.Count; i++)
{ {
@@ -38,7 +44,7 @@ public partial class Scenarios : UserControl
// case 1: no group - put without parent // case 1: no group - put without parent
if (string.IsNullOrEmpty(scenery.Group)) if (string.IsNullOrEmpty(scenery.Group))
{ {
sceneryList.Items.Add(new TreeViewItem topLevel.Add(new TreeViewItem
{ {
Header = Path.GetFileNameWithoutExtension(scenery.Path), Header = Path.GetFileNameWithoutExtension(scenery.Path),
Tag = i Tag = i
@@ -57,7 +63,7 @@ public partial class Scenarios : UserControl
}; };
groupNodes[scenery.Group] = groupNode; groupNodes[scenery.Group] = groupNode;
sceneryList.Items.Add(groupNode); topLevel.Add(groupNode);
} }
groupNode.Items.Add(new TreeViewItem groupNode.Items.Add(new TreeViewItem
@@ -67,17 +73,59 @@ public partial class Scenarios : UserControl
}); });
} }
// Sort the scenery tree alphabetically: group folders' inner sceneries
// first, then the top-level (group folders + ungrouped sceneries). The Tag
// keeps each node's original scenery index, so sorting only reorders the
// display - the vehicles/trainsets list stays in scenery order.
foreach (var node in groupNodes.Values)
{
var children = node.Items.Cast<TreeViewItem>()
.OrderBy(c => c.Header as string, StringComparer.OrdinalIgnoreCase)
.ToList();
node.Items.Clear();
foreach (var child in children)
node.Items.Add(child);
}
foreach (var item in topLevel.OrderBy(t => t.Header as string, StringComparer.OrdinalIgnoreCase))
sceneryList.Items.Add(item);
// refresh the consist preview when the view is shown again (e.g. after // refresh the consist preview when the view is shown again (e.g. after
// editing the consist in the depot). Switching tabs only toggles IsVisible // editing the consist in the depot). Switching tabs only toggles IsVisible
// (the view stays in the tree), so listen for that too. // (the view stays in the tree), so listen for that too.
AttachedToVisualTree += (_, _) => RefreshSelectedConsist(); AttachedToVisualTree += (_, _) => OnReentered();
PropertyChanged += (_, e) => PropertyChanged += (_, e) =>
{ {
if (e.Property == IsVisibleProperty && IsVisible) if (e.Property == IsVisibleProperty && IsVisible)
RefreshSelectedConsist(); OnReentered();
}; };
} }
// Called whenever this view is shown again (tab switch / re-attach). Edits made
// in the depot mutate the same Trainset objects, so refresh both the consist
// preview and the vehicle-list captions that depend on them.
private void OnReentered()
{
RefreshVehicleLabels();
RefreshSelectedConsist();
}
// Rebuilds the caption of each entry in the Vehicles list from its trainset's
// current vehicles, so a consist edited in the depot is reflected here too.
// Selection and order are preserved (only the text is updated).
private void RefreshVehicleLabels()
{
if (sceneryList.SelectedItem is not TreeViewItem { Tag: int tag } || tag >= Sceneries.Count)
return;
var scn = Sceneries[tag];
foreach (var obj in vehicleList.Items)
{
if (obj is ListBoxItem { Tag: int vi } lbi && vi < scn.Trainsets.Count)
lbi.Content = string.Join(" + ", scn.Trainsets[vi].Vehicles.Select(d => d.Name));
}
}
private void SceneryList_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) private void SceneryList_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{ {
var item = sceneryList.SelectedItem as TreeViewItem; var item = sceneryList.SelectedItem as TreeViewItem;
@@ -183,12 +231,14 @@ public partial class Scenarios : UserControl
try try
{ {
weatherForm.IsEnabled = true; weatherForm.IsEnabled = true;
weatherTime.Text = scenery.WeatherTime; weatherTime.SelectedTime = ParseTime(scenery.WeatherTime);
weatherDay.Value = scenery.Day; weatherDay.Value = scenery.Day;
weatherTemp.Value = scenery.Temperature; weatherTemp.Value = scenery.Temperature;
weatherFog.Value = System.Math.Clamp(scenery.FogEnd, 50, 2500); weatherFog.Value = System.Math.Clamp(scenery.FogEnd, 0, 10000);
SelectByTag(weatherSeason, scenery.Day.ToString()); SelectByTag(weatherSeason, scenery.Day.ToString());
SelectByTag(weatherOvercast, scenery.Overcast.ToString(CultureInfo.InvariantCulture)); SelectByTag(weatherOvercast, scenery.Overcast.ToString(CultureInfo.InvariantCulture));
// Day 0 maps to the "Today" season entry: preview the live day-of-year.
ApplyTodayPreview(scenery.Day == 0);
UpdateWeatherLabels(); UpdateWeatherLabels();
} }
finally finally
@@ -204,9 +254,11 @@ public partial class Scenarios : UserControl
return; return;
var scn = _currentScenery; var scn = _currentScenery;
if (!string.IsNullOrWhiteSpace(weatherTime.Text)) if (weatherTime.SelectedTime is { } t)
scn.WeatherTime = weatherTime.Text!.Trim(); scn.WeatherTime = $"{t.Hours:D2}:{t.Minutes:D2}";
scn.Day = (int)(weatherDay.Value ?? 0); // In "Today" mode the day field is only a preview, so keep the stored day
// at 0 (the scenery then follows the live date).
scn.Day = _todaySeason ? 0 : (int)(weatherDay.Value ?? 0);
scn.Temperature = weatherTemp.Value; scn.Temperature = weatherTemp.Value;
scn.FogEnd = (int)weatherFog.Value; scn.FogEnd = (int)weatherFog.Value;
if ((weatherOvercast.SelectedItem as ComboBoxItem)?.Tag is string ov && if ((weatherOvercast.SelectedItem as ComboBoxItem)?.Tag is string ov &&
@@ -235,7 +287,44 @@ public partial class Scenarios : UserControl
} }
// --- weather form event handlers --- // --- weather form event handlers ---
private void Weather_OnChanged(object? sender, RoutedEventArgs e) => CaptureWeather(); private void WeatherTime_OnChanged(object? sender, TimePickerSelectedValueChangedEventArgs e) => CaptureWeather();
// "Current time" button: drop the live wall-clock time into the picker (the
// SelectedTimeChanged handler then captures it onto the scenery).
private void WeatherTimeNow_OnClick(object? sender, RoutedEventArgs e)
{
var now = DateTime.Now;
weatherTime.SelectedTime = new TimeSpan(now.Hour, now.Minute, 0);
}
// Parses a stored "hh:mm" string into a TimeSpan, or null when it is missing
// or malformed (so the picker simply shows no selection).
private static TimeSpan? ParseTime(string? text)
{
if (string.IsNullOrWhiteSpace(text))
return null;
var m = Regex.Match(text, @"^\s*(\d{1,2})[:.](\d{2})");
if (m.Success &&
int.TryParse(m.Groups[1].Value, out int h) && h is >= 0 and <= 23 &&
int.TryParse(m.Groups[2].Value, out int min) && min is >= 0 and <= 59)
return new TimeSpan(h, min, 0);
return null;
}
// Toggles "Today" preview: the day field shows the live day-of-year (read-only)
// and CaptureWeather keeps the stored Day at 0; any other season re-enables it.
private void ApplyTodayPreview(bool today)
{
_todaySeason = today;
weatherDay.IsEnabled = !today;
if (today)
{
bool prev = _loadingWeather;
_loadingWeather = true;
weatherDay.Value = DateTime.Now.DayOfYear;
_loadingWeather = prev;
}
}
private void Weather_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) => CaptureWeather(); private void Weather_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) => CaptureWeather();
@@ -251,9 +340,17 @@ public partial class Scenarios : UserControl
if ((weatherSeason.SelectedItem as ComboBoxItem)?.Tag is string tag && if ((weatherSeason.SelectedItem as ComboBoxItem)?.Tag is string tag &&
int.TryParse(tag, out int day)) int.TryParse(tag, out int day))
{ {
_loadingWeather = true; if (day == 0) // "Today" - preview the live day-of-year, keep stored day at 0
weatherDay.Value = day; {
_loadingWeather = false; ApplyTodayPreview(true);
}
else
{
ApplyTodayPreview(false);
_loadingWeather = true;
weatherDay.Value = day;
_loadingWeather = false;
}
} }
CaptureWeather(); CaptureWeather();
} }

View File

@@ -22,6 +22,7 @@
<String key="NoWeatherData">No weather data in the scenery.</String> <String key="NoWeatherData">No weather data in the scenery.</String>
<String key="NoTimetable">No timetable for the selected consist.</String> <String key="NoTimetable">No timetable for the selected consist.</String>
<String key="Time">Time</String> <String key="Time">Time</String>
<String key="CurrentTime">Now</String>
<String key="Day">Day of year</String> <String key="Day">Day of year</String>
<String key="Sky">Sky</String> <String key="Sky">Sky</String>
<String key="Fog">Fog / visibility</String> <String key="Fog">Fog / visibility</String>

View File

@@ -22,6 +22,7 @@
<String key="NoWeatherData">Brak danych o pogodzie w scenerii.</String> <String key="NoWeatherData">Brak danych o pogodzie w scenerii.</String>
<String key="NoTimetable">Brak rozkładu jazdy dla wybranego składu.</String> <String key="NoTimetable">Brak rozkładu jazdy dla wybranego składu.</String>
<String key="Time">Godzina</String> <String key="Time">Godzina</String>
<String key="CurrentTime">Czas aktualny</String>
<String key="Day">Dzień roku</String> <String key="Day">Dzień roku</String>
<String key="Sky">Niebo</String> <String key="Sky">Niebo</String>
<String key="Fog">Mgła / widoczność</String> <String key="Fog">Mgła / widoczność</String>