mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-07-17 17:09:19 +02:00
Complete UI rebuild
This commit is contained in:
@@ -1,17 +1,33 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:sukiUi="clr-namespace:SukiUI;assembly=SukiUI"
|
||||
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
x:Class="StarterNG.App"
|
||||
x:CompileBindings="True"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
RequestedThemeVariant="Dark">
|
||||
<!-- Dark variant matches the original Starter's "Carbon" VCL style. -->
|
||||
|
||||
<Application.Styles>
|
||||
<sukiUi:SukiTheme ThemeColor="Green" />
|
||||
<FluentTheme />
|
||||
<materialIcons:MaterialIconStyles />
|
||||
<StyleInclude Source="avares://Starter/Styles/Carbon.axaml" />
|
||||
</Application.Styles>
|
||||
<Application.Resources>
|
||||
<PathGeometry x:Key="DiscordLogo" Figures="M 81.15 0 c -1.2376 2.1973 -2.3489 4.4704 -3.3591 6.794 c -9.5975 -1.4396 -19.3718 -1.4396 -28.9945 0 c -0.985 -2.3236 -2.1216 -4.5967 -3.3591 -6.794 c -9.0166 1.5407 -17.8059 4.2431 -26.1405 8.0568 C 2.779 32.5304 -1.6914 56.3725 0.5312 79.8863 c 9.6732 7.1476 20.5083 12.603 32.0505 16.0884 c 2.6014 -3.4854 4.8998 -7.1981 6.8698 -11.0623 c -3.738 -1.3891 -7.3497 -3.1318 -10.8098 -5.1523 c 0.9092 -0.6567 1.7932 -1.3386 2.6519 -1.9953 c 20.281 9.547 43.7696 9.547 64.0758 0 c 0.8587 0.7072 1.7427 1.3891 2.6519 1.9953 c -3.4601 2.0457 -7.0718 3.7632 -10.835 5.1776 c 1.97 3.8642 4.2683 7.5769 6.8698 11.0623 c 11.5419 -3.4854 22.3769 -8.9156 32.0509 -16.0631 c 2.626 -27.2771 -4.496 -50.9172 -18.817 -71.8548 C 98.9811 4.2684 90.1918 1.5659 81.1752 0.0505 l -0.0252 -0.0505 Z M 42.2802 65.4144 c -6.2383 0 -11.4159 -5.6575 -11.4159 -12.6535 s 4.9755 -12.6788 11.3907 -12.6788 s 11.5169 5.708 11.4159 12.6788 c -0.101 6.9708 -5.026 12.6535 -11.3907 12.6535 Z M 84.3576 65.4144 c -6.2637 0 -11.3907 -5.6575 -11.3907 -12.6535 s 4.9755 -12.6788 11.3907 -12.6788 s 11.4917 5.708 11.3906 12.6788 c -0.101 6.9708 -5.026 12.6535 -11.3906 12.6535 Z" FillRule="NonZero"/>
|
||||
|
||||
<!-- Override the system accent (the OS accent can be red) so every
|
||||
accent-driven element - list/tree selection, checkboxes,
|
||||
sliders - uses the MaSzyna green instead. -->
|
||||
<Color x:Key="SystemAccentColor">#177f00</Color>
|
||||
<Color x:Key="SystemAccentColorDark1">#136B00</Color>
|
||||
<Color x:Key="SystemAccentColorDark2">#0F5600</Color>
|
||||
<Color x:Key="SystemAccentColorDark3">#0B4200</Color>
|
||||
<Color x:Key="SystemAccentColorLight1">#2A9A0F</Color>
|
||||
<Color x:Key="SystemAccentColorLight2">#41C400</Color>
|
||||
<Color x:Key="SystemAccentColorLight3">#5FD42A</Color>
|
||||
<SolidColorBrush x:Key="AccentFillColorDefaultBrush" Color="#177f00" />
|
||||
<SolidColorBrush x:Key="AccentFillColorSecondaryBrush" Color="#1E9000" />
|
||||
<SolidColorBrush x:Key="AccentFillColorTertiaryBrush" Color="#136B00" />
|
||||
<SolidColorBrush x:Key="SystemControlHighlightListAccentLowBrush" Color="#177f00" />
|
||||
<SolidColorBrush x:Key="SystemControlHighlightListAccentMediumBrush" Color="#1E9000" />
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@@ -18,6 +19,21 @@ public class Scenery
|
||||
public string Description; // //$d - scenery description
|
||||
public string ImageName; // //$i - main-window image (scenery thumbnail)
|
||||
|
||||
// Weather / environment. Like the original Starter, these are editable and are
|
||||
// written into the scenery's "config" block on launch (see RewriteWeather).
|
||||
// Defaults mirror the original (15 °C, day 0, 10:30, clear sky).
|
||||
public string WeatherTime = "10:30"; // h:mm -> "time"/"scenario.time.override"
|
||||
public int Day = 0; // "movelight <day>" (day of year / season)
|
||||
public double Temperature = 15; // "scenario.weather.temperature"
|
||||
public int FogEnd = 2000; // visibility in metres (atmo fog range)
|
||||
public double Overcast = 0; // atmo overcast factor (-1.5 .. 1.5)
|
||||
|
||||
/// <summary>True when the scenery actually declared any weather command.</summary>
|
||||
public bool HasWeather;
|
||||
|
||||
/// <summary>Set once the user edits the weather, so export rewrites the config.</summary>
|
||||
public bool WeatherDirty;
|
||||
|
||||
// The file content with each trainset block replaced by a {{i}} placeholder,
|
||||
// used to rebuild the .scn on export.
|
||||
private readonly string _template;
|
||||
@@ -40,9 +56,14 @@ public class Scenery
|
||||
// matching //$it, etc.
|
||||
this.Group = MatchDirective(content, "l");
|
||||
this.Name = MatchDirective(content, "n");
|
||||
this.Description = MatchDirective(content, "d");
|
||||
// //$d may appear on several lines; each is one line of the description.
|
||||
this.Description = MatchAllDirectives(content, "d");
|
||||
this.ImageName = MatchDirective(content, "i");
|
||||
|
||||
// weather/environment is read from the raw text before trainset blocks
|
||||
// are stripped out below (the atmosphere commands live outside trainsets)
|
||||
ParseWeather(content);
|
||||
|
||||
|
||||
// parsing trainsets
|
||||
List<string> trainsetEntries = new List<string>();
|
||||
@@ -71,11 +92,125 @@ public class Scenery
|
||||
public string BuildExportContent()
|
||||
{
|
||||
string result = _template;
|
||||
|
||||
// Inject the (edited) weather into the config block before substituting
|
||||
// trainsets, so the {{i}} placeholders shield the trainset text from the
|
||||
// weather rewrite. Only done when the user actually changed the weather.
|
||||
if (WeatherDirty)
|
||||
result = RewriteWeather(result);
|
||||
|
||||
for (int i = 0; i < Trainsets.Count; i++)
|
||||
result = result.Replace("{{" + i + "}}", Trainsets[i].ToSceneryEntry());
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the scenery's environment commands into the editable weather fields.
|
||||
/// Mirrors the original Starter (config: movelight / scenario.weather.temperature
|
||||
/// / scenario.time.override, plus top-level time and the atmo fog/overcast block).
|
||||
/// </summary>
|
||||
private void ParseWeather(string content)
|
||||
{
|
||||
// start time: "scenario.time.override h:mm" wins, else top-level "time h:mm"
|
||||
var ovr = Regex.Match(content, @"(?i)scenario\.time\.override\s+(\d{1,2})[:.](\d{2})");
|
||||
var time = ovr.Success
|
||||
? ovr
|
||||
: Regex.Match(content, @"(?im)^\s*time\s+(\d{1,2})[:.](\d{2})\b");
|
||||
if (time.Success)
|
||||
{
|
||||
WeatherTime = $"{time.Groups[1].Value.PadLeft(2, '0')}:{time.Groups[2].Value}";
|
||||
HasWeather = true;
|
||||
}
|
||||
|
||||
// "movelight <day>" - day of the year (sun elevation / season)
|
||||
var move = Regex.Match(content, @"(?im)\bmovelight\s+(-?\d+)");
|
||||
if (move.Success && int.TryParse(move.Groups[1].Value, out int day))
|
||||
{
|
||||
Day = day;
|
||||
HasWeather = true;
|
||||
}
|
||||
|
||||
// "scenario.weather.temperature <°C>"
|
||||
var temp = Regex.Match(content, @"(?i)scenario\.weather\.temperature\s+(-?\d+(?:\.\d+)?)");
|
||||
if (temp.Success &&
|
||||
double.TryParse(temp.Groups[1].Value, System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out double t))
|
||||
{
|
||||
Temperature = t;
|
||||
HasWeather = true;
|
||||
}
|
||||
|
||||
// "atmo R G B fogStart fogEnd R G B overcast endatmo"
|
||||
var atmo = Regex.Match(content, @"(?is)\batmo\b(.*?)\bendatmo\b");
|
||||
if (atmo.Success)
|
||||
{
|
||||
var nums = Regex.Matches(atmo.Groups[1].Value, @"-?\d+(?:\.\d+)?")
|
||||
.Select(m => m.Value).ToList();
|
||||
if (nums.Count >= 5 && int.TryParse(nums[4], out int fog))
|
||||
{
|
||||
FogEnd = fog;
|
||||
HasWeather = true;
|
||||
}
|
||||
if (nums.Count >= 6 &&
|
||||
double.TryParse(nums[^1], System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out double oc))
|
||||
Overcast = oc;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the scenery's environment commands with a fresh config block built
|
||||
/// from the current weather fields (the C# equivalent of the original Starter's
|
||||
/// TLexParser.ChangeConfig). On any failure the original text is returned
|
||||
/// unchanged so a launch is never blocked by a bad rewrite.
|
||||
/// </summary>
|
||||
private string RewriteWeather(string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
// strip the existing weather commands
|
||||
string s = text;
|
||||
s = Regex.Replace(s, @"(?is)\bconfig\b.*?\bendconfig\b", " ");
|
||||
s = Regex.Replace(s, @"(?is)\btime\b\s+\d[^\r\n]*?\bendtime\b", " ");
|
||||
s = Regex.Replace(s, @"(?is)\batmo\b.*?\bendatmo\b", " ");
|
||||
s = Regex.Replace(s, @"(?im)^[ \t]*movelight\s+\S+", " ");
|
||||
s = Regex.Replace(s, @"(?i)scenario\.weather\.temperature\s+\S+", " ");
|
||||
s = Regex.Replace(s, @"(?i)scenario\.time\.override\s+\S+", " ");
|
||||
|
||||
var inv = System.Globalization.CultureInfo.InvariantCulture;
|
||||
string config =
|
||||
"config\r\n" +
|
||||
$"movelight {Day}\r\n" +
|
||||
$"scenario.weather.temperature {Temperature.ToString(inv)}\r\n" +
|
||||
$"scenario.time.override {WeatherTime}\r\n" +
|
||||
$"time {WeatherTime} 0 0 endtime\r\n" +
|
||||
$"atmo 0 0 0 {FogEnd} {FogEnd} 0 0 0 {Overcast.ToString(inv)} endatmo\r\n" +
|
||||
"endconfig\r\n";
|
||||
|
||||
return config + s;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects every //$<letter> line (e.g. all //$d lines) and joins them
|
||||
/// into one multi-line string. Returns null when none are present.
|
||||
/// </summary>
|
||||
private static string MatchAllDirectives(string content, string letter)
|
||||
{
|
||||
var matches = Regex.Matches(
|
||||
content,
|
||||
@"^//\$" + letter + @"\b[ \t]*([^\r\n]*)",
|
||||
RegexOptions.Multiline
|
||||
);
|
||||
if (matches.Count == 0)
|
||||
return null;
|
||||
return string.Join("\n", matches.Select(m => m.Groups[1].Value.TrimEnd()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a single starter directive value (the text after //$<letter>).
|
||||
/// Returns null when the directive is absent. The trailing whitespace
|
||||
|
||||
@@ -1,112 +1,116 @@
|
||||
<sukiUi:SukiWindow
|
||||
<Window
|
||||
x:Class="StarterNG.MainWindow"
|
||||
x:CompileBindings="True"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:sukiUi="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
||||
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:views="clr-namespace:StarterNG.Views"
|
||||
xmlns:starterNg="clr-namespace:StarterNG"
|
||||
BackgroundStyle="Flat"
|
||||
Title="MaSzyna - Symulator pojazdów szynowych" Height="800" Width="1200" WindowStartupLocation="CenterScreen">
|
||||
<sukiUi:SukiWindow.RightWindowTitleBarControls>
|
||||
Title="Starter MaSzyna"
|
||||
Width="1200" Height="768" MinWidth="1200" MinHeight="666"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<!-- The client area is extended over the title bar in code-behind (the
|
||||
ExtendClientArea* hints), so the logo + nav sit on the top bar while the
|
||||
OS minimise / maximise / close buttons stay in the top-right corner. -->
|
||||
|
||||
<DockPanel LastChildFill="True">
|
||||
|
||||
<!-- ── Top bar: thin caption strip (row 0) + toolbar (row 1) ────────────
|
||||
The OS minimise / maximise / close buttons render at the top-right of
|
||||
the caption strip, so they sit ABOVE the social-icon cluster, which
|
||||
lives on the right of the toolbar row. -->
|
||||
<Border DockPanel.Dock="Top"
|
||||
Background="{StaticResource CarbonPanelBg}"
|
||||
BorderBrush="{StaticResource CarbonBorderLight}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<Grid RowDefinitions="32,48">
|
||||
|
||||
<!-- Row 0: title / caption strip (draggable; OS buttons top-right) -->
|
||||
<TextBlock Grid.Row="0" Text="Starter MaSzyna" VerticalAlignment="Center"
|
||||
Margin="14,0,0,0" FontSize="11"
|
||||
Foreground="{StaticResource CarbonFgDim}" />
|
||||
|
||||
<!-- Row 1: logo + navigation (left) and the right cluster (right) -->
|
||||
<Grid Grid.Row="1" ColumnDefinitions="Auto,Auto,*,Auto">
|
||||
|
||||
<!-- Logo -->
|
||||
<Image Grid.Column="0" Source="/Assets/Logo/maszyna_white.png"
|
||||
Height="34" Margin="14,0,18,0" VerticalAlignment="Center" />
|
||||
|
||||
<!-- Page tabs -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Stretch">
|
||||
<RadioButton x:Name="NavScenarios" GroupName="nav" Theme="{StaticResource NavTab}"
|
||||
Tag="scenarios" IsChecked="True"
|
||||
IsCheckedChanged="Nav_OnCheckedChanged"
|
||||
Content="{Binding [NavScenarios], Source={x:Static starterNg:App.Loc}}" />
|
||||
<RadioButton x:Name="NavDepot" GroupName="nav" Theme="{StaticResource NavTab}"
|
||||
Tag="depot"
|
||||
IsCheckedChanged="Nav_OnCheckedChanged"
|
||||
Content="{Binding [NavDepot], Source={x:Static starterNg:App.Loc}}" />
|
||||
<RadioButton x:Name="NavSettings" GroupName="nav" Theme="{StaticResource NavTab}"
|
||||
Tag="settings"
|
||||
IsCheckedChanged="Nav_OnCheckedChanged"
|
||||
Content="{Binding [NavSettings], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Button Classes="Basic" Width="48" Cursor="Hand"
|
||||
ToolTip.Tip="{Binding [Help], Source={x:Static starterNg:App.Loc}}"
|
||||
Tag="https://wiki.eu07.pl/" Click="linkClick"
|
||||
Content="?" FontWeight="Bold" FontSize="16"
|
||||
HorizontalContentAlignment="Center" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Right cluster: version, language, log, social links.
|
||||
In the toolbar row, so it sits below the OS caption buttons. -->
|
||||
<StackPanel Grid.Column="3" Orientation="Horizontal" VerticalAlignment="Center"
|
||||
Margin="0,0,8,0" Spacing="4">
|
||||
<StackPanel Orientation="Vertical" VerticalAlignment="Center" Margin="0,0,8,0">
|
||||
<Label Content="{Binding [Version], Source={x:Static starterNg:App.Loc}}"
|
||||
FontSize="10" Foreground="{StaticResource CarbonFgDim}" Padding="0" />
|
||||
<Label Content="{x:Static starterNg:AppInfo.Version}"
|
||||
FontSize="10" Foreground="{StaticResource CarbonFg}" Padding="0" />
|
||||
</StackPanel>
|
||||
|
||||
<ComboBox x:Name="LangCombo" Width="100" VerticalAlignment="Center"
|
||||
SelectionChanged="LangCombo_OnSelectionChanged">
|
||||
<ComboBoxItem>Polski</ComboBoxItem>
|
||||
<ComboBoxItem>English</ComboBoxItem>
|
||||
</ComboBox>
|
||||
|
||||
<Button Classes="Basic" Click="openLastLogClick"
|
||||
ToolTip.Tip="{Binding [ShowLatestLogTooltip], Source={x:Static starterNg:App.Loc}}">
|
||||
<materialIcons:MaterialIcon Kind="FileDocument"/>
|
||||
<materialIcons:MaterialIcon Kind="FileDocument" />
|
||||
</Button>
|
||||
<Label VerticalAlignment="Center" Content="{x:Static starterNg:AppInfo.Version}"/>
|
||||
</sukiUi:SukiWindow.RightWindowTitleBarControls>
|
||||
<sukiUi:SukiSideMenu IsMenuExpanded="False">
|
||||
<sukiUi:SukiSideMenu.Items>
|
||||
<!-- SCENARIUSZE -->
|
||||
<sukiUi:SukiSideMenuItem
|
||||
Header="{Binding [NavScenarios], Source={x:Static starterNg:App.Loc}}"
|
||||
Classes="Compact">
|
||||
<sukiUi:SukiSideMenuItem.Icon>
|
||||
<materialIcons:MaterialIcon Kind="ListBox" />
|
||||
</sukiUi:SukiSideMenuItem.Icon>
|
||||
<sukiUi:SukiSideMenuItem.PageContent>
|
||||
<views:Scenarios />
|
||||
</sukiUi:SukiSideMenuItem.PageContent>
|
||||
</sukiUi:SukiSideMenuItem>
|
||||
|
||||
<!-- Lokomotywownia -->
|
||||
<sukiUi:SukiSideMenuItem
|
||||
Header="{Binding [NavDepot], Source={x:Static starterNg:App.Loc}}"
|
||||
Classes="Compact">
|
||||
<sukiUi:SukiSideMenuItem.Icon>
|
||||
<materialIcons:MaterialIcon Kind="Train" />
|
||||
</sukiUi:SukiSideMenuItem.Icon>
|
||||
<sukiUi:SukiSideMenuItem.PageContent>
|
||||
<views:Depot />
|
||||
</sukiUi:SukiSideMenuItem.PageContent>
|
||||
</sukiUi:SukiSideMenuItem>
|
||||
|
||||
<!-- Ustawienia -->
|
||||
<sukiUi:SukiSideMenuItem
|
||||
Header="{Binding [NavSettings], Source={x:Static starterNg:App.Loc}}"
|
||||
Classes="Compact">
|
||||
<sukiUi:SukiSideMenuItem.Icon>
|
||||
<materialIcons:MaterialIcon Kind="Settings" />
|
||||
</sukiUi:SukiSideMenuItem.Icon>
|
||||
<sukiUi:SukiSideMenuItem.PageContent>
|
||||
<views:Settings/>
|
||||
</sukiUi:SukiSideMenuItem.PageContent>
|
||||
</sukiUi:SukiSideMenuItem>
|
||||
|
||||
<!-- Sterowanie -->
|
||||
<sukiUi:SukiSideMenuItem
|
||||
Header="{Binding [NavControls], Source={x:Static starterNg:App.Loc}}"
|
||||
Classes="Compact">
|
||||
<sukiUi:SukiSideMenuItem.Icon>
|
||||
<materialIcons:MaterialIcon Kind="Controller" />
|
||||
</sukiUi:SukiSideMenuItem.Icon>
|
||||
<sukiUi:SukiSideMenuItem.PageContent>
|
||||
<Label>Work in progress</Label>
|
||||
</sukiUi:SukiSideMenuItem.PageContent>
|
||||
</sukiUi:SukiSideMenuItem>
|
||||
|
||||
|
||||
</sukiUi:SukiSideMenu.Items>
|
||||
|
||||
|
||||
<sukiUi:SukiSideMenu.HeaderContent>
|
||||
<!-- Logo PNG. Replace Assets/Logo/maszyna_white.png (white logo for the
|
||||
dark side-menu header). A maszyna_black.png variant also lives in
|
||||
Assets/Logo for light backgrounds - swap the Source if needed. -->
|
||||
<Image
|
||||
Source="/Assets/Logo/maszyna_white.png"
|
||||
Width="200"
|
||||
Margin="16"
|
||||
Height="60" />
|
||||
|
||||
</sukiUi:SukiSideMenu.HeaderContent>
|
||||
|
||||
<sukiUi:SukiSideMenu.FooterContent>
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<!-- eu07.pl -->
|
||||
<Button Classes="Basic" Cursor="Hand" Tag="http://eu07.pl/" Click="linkClick">
|
||||
<materialIcons:MaterialIcon Kind="Web" />
|
||||
</Button>
|
||||
<!-- Discord -->
|
||||
<Button Classes="Basic" Cursor="Hand" Tag="https://discord.gg/eu07" Click="linkClick">
|
||||
<PathIcon Data="{StaticResource DiscordLogo}" Height="12" />
|
||||
</Button>
|
||||
<!-- Facebook -->
|
||||
<Button Classes="Basic" Cursor="Hand" Tag="http://www.facebook.com/MaSzynaeu07pl" Click="linkClick">
|
||||
<materialIcons:MaterialIcon Kind="Facebook" />
|
||||
</Button>
|
||||
<!-- Youtube -->
|
||||
<Button Classes="Basic" Cursor="Hand" Tag="http://www.youtube.com/@eu07official" Click="linkClick">
|
||||
<materialIcons:MaterialIcon Kind="Youtube" />
|
||||
</Button>
|
||||
<!-- Github -->
|
||||
<Button Classes="Basic" Cursor="Hand" Tag="http://github.com/MaSzyna-EU07/" Click="linkClick">
|
||||
<materialIcons:MaterialIcon Kind="Github" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</sukiUi:SukiSideMenu.FooterContent>
|
||||
</sukiUi:SukiSideMenu>
|
||||
</sukiUi:SukiWindow>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- ── Full-width START button (bottom) ─────────────────────────────── -->
|
||||
<Button DockPanel.Dock="Bottom" Classes="Start" Height="44" Margin="6"
|
||||
Click="StartButton_OnClick"
|
||||
Content="{Binding [Start], Source={x:Static starterNg:App.Loc}}" />
|
||||
|
||||
<!-- ── Page host (one view visible at a time, like the original PageControl) -->
|
||||
<Panel Margin="6,6,6,0">
|
||||
<views:Scenarios x:Name="ScenariosView" />
|
||||
<views:Depot x:Name="DepotView" IsVisible="False" />
|
||||
<views:Settings x:Name="SettingsView" IsVisible="False" />
|
||||
</Panel>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
||||
@@ -1,21 +1,59 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using SukiUI.Controls;
|
||||
using StarterNG.Classes;
|
||||
|
||||
namespace StarterNG;
|
||||
|
||||
public partial class MainWindow : SukiWindow
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
InitializeComponent();
|
||||
|
||||
// Extend the client area over the title bar so the logo + navigation sit
|
||||
// on the top bar, while the OS caption buttons (min / max / close) stay in
|
||||
// the top-right corner. The default chrome hints already keep the system
|
||||
// caption buttons, so only the two supported hints are set here (this
|
||||
// Avalonia build no longer exposes ExtendClientAreaChromeHints).
|
||||
ExtendClientAreaToDecorationsHint = true;
|
||||
// Match the thin caption strip (row 0 of the top bar): the OS min/max/close
|
||||
// buttons fill this height at the top-right, sitting above the social row.
|
||||
ExtendClientAreaTitleBarHeightHint = 32;
|
||||
|
||||
// Mirror the active language into the top-bar selector.
|
||||
LangCombo.SelectedIndex = App.Loc.CurrentLanguage == "Polski" ? 0 : 1;
|
||||
}
|
||||
|
||||
// ── Navigation: show the page whose RadioButton was just checked ──────────
|
||||
private void Nav_OnCheckedChanged(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not RadioButton { IsChecked: true } rb)
|
||||
return;
|
||||
|
||||
// Guard against the controls not being created yet during initial load.
|
||||
if (ScenariosView is null || DepotView is null || SettingsView is null)
|
||||
return;
|
||||
|
||||
string page = rb.Tag as string ?? "scenarios";
|
||||
ScenariosView.IsVisible = page == "scenarios";
|
||||
DepotView.IsVisible = page == "depot";
|
||||
SettingsView.IsVisible = page == "settings";
|
||||
}
|
||||
|
||||
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.Content?.ToString();
|
||||
if (!string.IsNullOrEmpty(lang))
|
||||
App.ApplyLanguage(lang);
|
||||
}
|
||||
|
||||
private void linkClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
@@ -42,4 +80,53 @@ public partial class MainWindow : SukiWindow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── START: export the (possibly depot-modified) scenery and launch the sim.
|
||||
// Global on the main window, like the original Starter's bottom START button.
|
||||
private void StartButton_OnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var scenery = AppState.Instance.CurrentScenery;
|
||||
if (scenery is null)
|
||||
return;
|
||||
var trainset = AppState.Instance.CurrentTrainset;
|
||||
|
||||
// write scenery/$<name>.scn with the replaced trainsets
|
||||
string dir = Path.GetDirectoryName(scenery.Path) ?? "scenery";
|
||||
string exportName = "$" + Path.GetFileName(scenery.Path);
|
||||
string exportPath = Path.Combine(dir, exportName);
|
||||
try
|
||||
{
|
||||
File.WriteAllText(exportPath, scenery.BuildExportContent(), Encoding.GetEncoding(1250));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return; // couldn't write the scenery file
|
||||
}
|
||||
|
||||
// the player's vehicle is the node name of the driven car in the consist
|
||||
string? vehicle = trainset?.Vehicles
|
||||
.FirstOrDefault(v => v.DriverType is eDriverType.Headdriver or eDriverType.Reardriver)?.Name
|
||||
?? trainset?.Vehicles.FirstOrDefault()?.Name;
|
||||
|
||||
// make sure the latest settings are on disk before the sim reads them
|
||||
Settings.Instance.CaptureAndSave();
|
||||
|
||||
// launch the game: -s $<name>.scn -v <vehicle>
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(Settings.Instance.ExecutablePath)
|
||||
{
|
||||
Arguments = $"-s {exportName} -v {vehicle}",
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
return; // executable not found / not configured yet
|
||||
}
|
||||
|
||||
// honour the "close starter automatically" preference
|
||||
if (Settings.Instance.AutoCloseStarter)
|
||||
Close();
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<AssemblyVersion>1.0.0.1 </AssemblyVersion>
|
||||
<FileVersion>1.0.0.1</FileVersion>
|
||||
<AssemblyName>Starter</AssemblyName>
|
||||
<Company>eu07.pl</Company>
|
||||
</PropertyGroup>
|
||||
@@ -32,7 +32,6 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="HotAvalonia" Version="3.0.2" />
|
||||
<PackageReference Include="Material.Icons.Avalonia" Version="3.0.2" />
|
||||
<PackageReference Include="SukiUI" Version="7.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
190
StarterNG/Styles/Carbon.axaml
Normal file
190
StarterNG/Styles/Carbon.axaml
Normal file
@@ -0,0 +1,190 @@
|
||||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<!--
|
||||
Carbon theme for StarterNG.
|
||||
|
||||
Recreates the dark "Carbon" VCL look of the original Delphi Starter without
|
||||
SukiUI: a flat charcoal palette, square bordered group panels, top tab-style
|
||||
navigation and a full-width START button. Plain Avalonia controls only, so
|
||||
every view's code-behind keeps working unchanged.
|
||||
-->
|
||||
<Styles.Resources>
|
||||
<!-- ── Palette ──────────────────────────────────────────────────── -->
|
||||
<Color x:Key="CarbonAppBgColor">#15171B</Color>
|
||||
<Color x:Key="CarbonPanelBgColor">#1E2327</Color>
|
||||
<Color x:Key="CarbonHeaderBgColor">#2A3036</Color>
|
||||
<Color x:Key="CarbonInputBgColor">#262C31</Color>
|
||||
<Color x:Key="CarbonAccentColor">#177f00</Color>
|
||||
|
||||
<SolidColorBrush x:Key="CarbonAppBg" Color="#15171B" />
|
||||
<SolidColorBrush x:Key="CarbonPanelBg" Color="#1E2327" />
|
||||
<SolidColorBrush x:Key="CarbonHeaderBg" Color="#2A3036" />
|
||||
<SolidColorBrush x:Key="CarbonHoverBg" Color="#2A3036" />
|
||||
<SolidColorBrush x:Key="CarbonInputBg" Color="#262C31" />
|
||||
<SolidColorBrush x:Key="CarbonBorderLight" Color="#3A424A" />
|
||||
<SolidColorBrush x:Key="CarbonBorderDark" Color="#0E0F12" />
|
||||
<SolidColorBrush x:Key="CarbonFg" Color="#E6E8EA" />
|
||||
<SolidColorBrush x:Key="CarbonFgDim" Color="#9098A0" />
|
||||
<!-- Accents in MaSzyna green: a readable dark green for fills, bright green
|
||||
for hover/highlights. -->
|
||||
<SolidColorBrush x:Key="CarbonAccent" Color="#177f00" />
|
||||
<SolidColorBrush x:Key="CarbonAccentHover" Color="#41c400" />
|
||||
<SolidColorBrush x:Key="CarbonAccentBright" Color="#41c400" />
|
||||
|
||||
<!-- ── GroupBox (replaces SukiUI GroupBox) ──────────────────────────
|
||||
Use on a HeaderedContentControl via Theme="{StaticResource GroupBox}". -->
|
||||
<ControlTheme x:Key="GroupBox" TargetType="HeaderedContentControl">
|
||||
<Setter Property="Foreground" Value="{StaticResource CarbonFg}" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border Background="{StaticResource CarbonPanelBg}"
|
||||
BorderBrush="{StaticResource CarbonBorderLight}"
|
||||
BorderThickness="1" CornerRadius="2">
|
||||
<DockPanel LastChildFill="True">
|
||||
<Border DockPanel.Dock="Top"
|
||||
Background="{StaticResource CarbonHeaderBg}"
|
||||
BorderBrush="{StaticResource CarbonBorderLight}"
|
||||
BorderThickness="0,0,0,1"
|
||||
Padding="8,5">
|
||||
<TextBlock Text="{TemplateBinding Header}"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{StaticResource CarbonFg}" />
|
||||
</Border>
|
||||
<ContentPresenter Content="{TemplateBinding Content}"
|
||||
Margin="8" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
|
||||
<!-- ── Top navigation tab (RadioButton) ─────────────────────────────
|
||||
Use via Theme="{StaticResource NavTab}". Single GroupName = one active. -->
|
||||
<ControlTheme x:Key="NavTab" TargetType="RadioButton">
|
||||
<Setter Property="Foreground" Value="{StaticResource CarbonFgDim}" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="MinWidth" Value="150" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Border Name="root" Background="Transparent"
|
||||
BorderBrush="Transparent" BorderThickness="0,0,0,2">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Content="{TemplateBinding Content}" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
<Style Selector="^:pointerover /template/ Border#root">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonHoverBg}" />
|
||||
</Style>
|
||||
<Style Selector="^:checked /template/ Border#root">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonHoverBg}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource CarbonAccentBright}" />
|
||||
</Style>
|
||||
<Style Selector="^:checked">
|
||||
<Setter Property="Foreground" Value="{StaticResource CarbonFg}" />
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
</Styles.Resources>
|
||||
|
||||
<!-- ── Window / page surfaces ─────────────────────────────────────────── -->
|
||||
<Style Selector="Window">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAppBg}" />
|
||||
<Setter Property="Foreground" Value="{StaticResource CarbonFg}" />
|
||||
<Setter Property="FontFamily" Value="Tahoma, Segoe UI, Inter" />
|
||||
</Style>
|
||||
|
||||
<!-- ── START button (full-width, bold) ────────────────────────────────── -->
|
||||
<Style Selector="Button.Start">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccent}" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="FontSize" Value="16" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="CornerRadius" Value="2" />
|
||||
</Style>
|
||||
<Style Selector="Button.Start /template/ ContentPresenter#PART_ContentPresenter">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccent}" />
|
||||
</Style>
|
||||
<Style Selector="Button.Start:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccentHover}" />
|
||||
<Setter Property="TextBlock.Foreground" Value="White" />
|
||||
</Style>
|
||||
<Style Selector="Button.Start:pressed /template/ ContentPresenter#PART_ContentPresenter">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccent}" />
|
||||
</Style>
|
||||
|
||||
<!-- ── Accent button (Save etc.) ──────────────────────────────────────── -->
|
||||
<Style Selector="Button.Accent /template/ ContentPresenter#PART_ContentPresenter">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccent}" />
|
||||
<Setter Property="TextBlock.Foreground" Value="White" />
|
||||
</Style>
|
||||
<Style Selector="Button.Accent:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccentHover}" />
|
||||
<Setter Property="TextBlock.Foreground" Value="White" />
|
||||
</Style>
|
||||
|
||||
<!-- ── Flat button (toolbar/secondary) ────────────────────────────────── -->
|
||||
<Style Selector="Button.Flat">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonInputBg}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource CarbonBorderLight}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="2" />
|
||||
</Style>
|
||||
|
||||
<!-- ── Basic button (borderless icon buttons) ─────────────────────────── -->
|
||||
<Style Selector="Button.Basic">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="6" />
|
||||
<Setter Property="Foreground" Value="{StaticResource CarbonFgDim}" />
|
||||
</Style>
|
||||
<Style Selector="Button.Basic /template/ ContentPresenter#PART_ContentPresenter">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
<Style Selector="Button.Basic:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonHoverBg}" />
|
||||
<Setter Property="TextBlock.Foreground" Value="{StaticResource CarbonFg}" />
|
||||
</Style>
|
||||
|
||||
<!-- ── Selection highlight in MaSzyna green (not the OS accent) ────────── -->
|
||||
<Style Selector="ListBoxItem:selected /template/ ContentPresenter#PART_ContentPresenter">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccent}" />
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:selected:pointerover /template/ ContentPresenter#PART_ContentPresenter">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccentHover}" />
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:selected:focus /template/ ContentPresenter#PART_ContentPresenter">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccent}" />
|
||||
</Style>
|
||||
<Style Selector="TreeViewItem:selected /template/ Border#PART_LayoutRoot">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccent}" />
|
||||
</Style>
|
||||
<Style Selector="TreeViewItem:selected:pointerover /template/ Border#PART_LayoutRoot">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccentHover}" />
|
||||
</Style>
|
||||
<Style Selector="ComboBoxItem:selected /template/ ContentPresenter#PART_ContentPresenter">
|
||||
<Setter Property="Background" Value="{StaticResource CarbonAccent}" />
|
||||
</Style>
|
||||
|
||||
<!-- ── Tabs: smaller headers (Fluent's default is oversized) ──────────── -->
|
||||
<Style Selector="TabItem">
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="MinHeight" Value="0" />
|
||||
<Setter Property="Padding" Value="10,6" />
|
||||
</Style>
|
||||
|
||||
<!-- ── Compact list (tight rows that fill the available space) ─────────── -->
|
||||
<Style Selector="ListBox.Compact">
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style>
|
||||
<Style Selector="ListBox.Compact ListBoxItem">
|
||||
<Setter Property="Padding" Value="6,2" />
|
||||
<Setter Property="MinHeight" Value="0" />
|
||||
<Setter Property="FontSize" Value="12" />
|
||||
</Style>
|
||||
</Styles>
|
||||
@@ -1,14 +1,13 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
<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:sukiUi="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:starterNg="clr-namespace:StarterNG"
|
||||
xmlns:views="clr-namespace:StarterNG.Views"
|
||||
x:CompileBindings="True"
|
||||
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="700"
|
||||
x:Class="StarterNG.Views.Depot">
|
||||
<Grid Margin="16" ColumnSpacing="16" RowSpacing="16">
|
||||
<Grid Margin="0" ColumnSpacing="8" RowSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="340" />
|
||||
<ColumnDefinition />
|
||||
@@ -20,28 +19,46 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Vehicle database browser (left, rows 0-1) -->
|
||||
<sukiUi:GroupBox Grid.RowSpan="2"
|
||||
<HeaderedContentControl Grid.RowSpan="2" Theme="{StaticResource GroupBox}"
|
||||
Header="{Binding [VehicleDatabase], Source={x:Static starterNg:App.Loc}}">
|
||||
<DockPanel LastChildFill="True">
|
||||
<Grid DockPanel.Dock="Top" ColumnDefinitions="*,*" ColumnSpacing="6" Margin="0,0,0,6">
|
||||
<ComboBox Name="categoryCombo" Grid.Column="0" HorizontalAlignment="Stretch"
|
||||
PlaceholderText="{Binding [Category], Source={x:Static starterNg:App.Loc}}"
|
||||
SelectionChanged="CategoryCombo_OnSelectionChanged" />
|
||||
<ComboBox Name="classCombo" Grid.Column="1" HorizontalAlignment="Stretch"
|
||||
PlaceholderText="{Binding [Class], Source={x:Static starterNg:App.Loc}}"
|
||||
SelectionChanged="ClassCombo_OnSelectionChanged" />
|
||||
</Grid>
|
||||
<TextBox Name="searchBox" DockPanel.Dock="Top" Margin="0,0,0,8"
|
||||
Watermark="{Binding [Search], Source={x:Static starterNg:App.Loc}}"
|
||||
TextChanged="SearchBox_OnTextChanged" />
|
||||
<!-- Folded groups (built in code-behind); collapsed groups hold no
|
||||
controls, so the list stays light and scrolls smoothly. -->
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Name="browserStack" Spacing="2" />
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Mini preview of the selected entry + the single Add button.
|
||||
Both are docked at the bottom, under the flat vehicle list. -->
|
||||
<Button Name="addVehicleButton" DockPanel.Dock="Bottom" Classes="Flat Accent"
|
||||
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
|
||||
Margin="0,8,0,0" IsEnabled="False" Cursor="Hand"
|
||||
Click="AddVehicleButton_OnClick"
|
||||
Content="{Binding [AddVehicle], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Border Name="miniPreviewPanel" DockPanel.Dock="Bottom" Height="62" Margin="0,6,0,0"
|
||||
Background="{StaticResource CarbonInputBg}"
|
||||
BorderBrush="{StaticResource CarbonBorderLight}" BorderThickness="1"
|
||||
CornerRadius="2">
|
||||
<Image Name="miniPreview" Stretch="Uniform" Margin="6" />
|
||||
</Border>
|
||||
|
||||
<!-- Flat list of vehicle entries (plain text, no expanders).
|
||||
Selecting one shows its mini and enables Add; double-click
|
||||
replaces the selected consist vehicle. -->
|
||||
<ListBox Name="vehicleListBox" Classes="Compact"
|
||||
SelectionChanged="VehicleListBox_OnSelectionChanged"
|
||||
DoubleTapped="VehicleListBox_OnDoubleTapped" />
|
||||
</DockPanel>
|
||||
</sukiUi:GroupBox>
|
||||
</HeaderedContentControl>
|
||||
|
||||
<!-- Scenery + load consist (top-right) -->
|
||||
<sukiUi:GroupBox Grid.Column="1" Grid.Row="0"
|
||||
<HeaderedContentControl Grid.Column="1" Grid.Row="0" Theme="{StaticResource GroupBox}"
|
||||
Header="{Binding [LoadFromScenery], Source={x:Static starterNg:App.Loc}}">
|
||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto" ColumnSpacing="8" RowSpacing="6">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"
|
||||
@@ -54,10 +71,10 @@
|
||||
DropDownOpened="SceneryConsistCombo_OnDropDownOpened"
|
||||
SelectionChanged="SceneryConsistCombo_OnSelectionChanged" />
|
||||
</Grid>
|
||||
</sukiUi:GroupBox>
|
||||
</HeaderedContentControl>
|
||||
|
||||
<!-- Brakes / Loads editor (middle-right) -->
|
||||
<sukiUi:GroupBox Grid.Column="1" Grid.Row="1"
|
||||
<HeaderedContentControl Grid.Column="1" Grid.Row="1" Theme="{StaticResource GroupBox}"
|
||||
Header="{Binding [SelectedVehicle], Source={x:Static starterNg:App.Loc}}">
|
||||
<DockPanel LastChildFill="True">
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Spacing="8" Margin="0,0,0,8">
|
||||
@@ -81,10 +98,10 @@
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</sukiUi:GroupBox>
|
||||
</HeaderedContentControl>
|
||||
|
||||
<!-- Consist (full-width bottom, horizontally scrollable) -->
|
||||
<sukiUi:GroupBox Grid.Row="2" Grid.ColumnSpan="2"
|
||||
<HeaderedContentControl Grid.Row="2" Grid.ColumnSpan="2" Theme="{StaticResource GroupBox}"
|
||||
Header="{Binding [Consist], Source={x:Static starterNg:App.Loc}}">
|
||||
<Panel>
|
||||
<TextBlock Name="emptyHint" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
@@ -95,6 +112,6 @@
|
||||
VerticalAlignment="Center" Spacing="0" />
|
||||
</ScrollViewer>
|
||||
</Panel>
|
||||
</sukiUi:GroupBox>
|
||||
</HeaderedContentControl>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -29,8 +29,7 @@ public partial class Depot : UserControl
|
||||
|
||||
// Shared, size-limited thumbnail cache (decoded down to display height).
|
||||
private readonly Dictionary<string, Bitmap?> _miniCache = new();
|
||||
private const int BrowserThumbHeight = 38;
|
||||
private const int ConsistThumbHeight = 50;
|
||||
private const int ConsistThumbHeight = 34;
|
||||
|
||||
private readonly Cursor _hand = new(StandardCursorType.Hand);
|
||||
private int _nameCounter;
|
||||
@@ -44,18 +43,21 @@ public partial class Depot : UserControl
|
||||
// scenery-consist previews are built lazily on first dropdown open
|
||||
private bool _consistPreviewsBuilt;
|
||||
|
||||
// auto-expand search results only when there are few of them
|
||||
private const int AutoExpandLimit = 40;
|
||||
private const int MaxRowsPerGroup = 250;
|
||||
|
||||
// active database filters
|
||||
private Func<string?, bool>? _categoryFilter; // matches a category letter, null = all
|
||||
private string? _classFilter; // group mini (e.g. EU07), null = all
|
||||
private Func<string?, bool>? _categoryFilter; // matches a category letter, null = none chosen
|
||||
private string? _classFilter; // group mini (e.g. EU07), null = all in category
|
||||
|
||||
// the vehicle currently highlighted in the flat list (drives the mini preview
|
||||
// and the single Add button)
|
||||
private VehicleTexture? _browserSelected;
|
||||
|
||||
// hard cap on how many entries the flat list shows at once
|
||||
private const int MaxListRows = 2000;
|
||||
|
||||
// card highlight brushes (theme-neutral overlays)
|
||||
private static readonly IBrush CardBorder = new SolidColorBrush(Color.Parse("#33888888"));
|
||||
private static readonly IBrush CardBorderSel = new SolidColorBrush(Color.Parse("#AA4D8BFF"));
|
||||
private static readonly IBrush CardBgSel = new SolidColorBrush(Color.Parse("#224D8BFF"));
|
||||
private static readonly IBrush CardBorderSel = new SolidColorBrush(Color.Parse("#AA41C400"));
|
||||
private static readonly IBrush CardBgSel = new SolidColorBrush(Color.Parse("#2241C400"));
|
||||
private static readonly IBrush Placeholder = new SolidColorBrush(Color.Parse("#22808080"));
|
||||
|
||||
// Vehicle types, keyed by the JSON "category" letter. Powered/technical
|
||||
@@ -132,7 +134,7 @@ public partial class Depot : UserControl
|
||||
_searchTimer.Tick += (_, _) =>
|
||||
{
|
||||
_searchTimer!.Stop();
|
||||
BuildBrowser();
|
||||
BuildList();
|
||||
};
|
||||
|
||||
// Defer population so the tab renders immediately, then fills in.
|
||||
@@ -275,12 +277,13 @@ public partial class Depot : UserControl
|
||||
|
||||
private void PopulateCategoryCombo()
|
||||
{
|
||||
// No "All" entry: nothing is selected by default, so the list starts empty
|
||||
// (matching the original Starter's depot).
|
||||
_suppress = true;
|
||||
categoryCombo.Items.Clear();
|
||||
categoryCombo.Items.Add(new ComboBoxItem { Content = App.Loc["All"], Tag = null });
|
||||
foreach (var (locKey, match) in CategoryDefs)
|
||||
categoryCombo.Items.Add(new ComboBoxItem { Content = App.Loc[locKey], Tag = match });
|
||||
categoryCombo.SelectedIndex = 0;
|
||||
categoryCombo.SelectedIndex = -1;
|
||||
_suppress = false;
|
||||
|
||||
_categoryFilter = null;
|
||||
@@ -289,18 +292,16 @@ public partial class Depot : UserControl
|
||||
|
||||
private void RebuildClassCombo()
|
||||
{
|
||||
// No "All" entry: an unselected class means "every class in the category".
|
||||
_suppress = true;
|
||||
classCombo.Items.Clear();
|
||||
classCombo.Items.Add(new ComboBoxItem { Content = App.Loc["All"], Tag = null });
|
||||
|
||||
foreach (string cls in ClassesForCategory(_categoryFilter))
|
||||
classCombo.Items.Add(new ComboBoxItem { Content = cls, Tag = cls });
|
||||
|
||||
classCombo.SelectedIndex = 0;
|
||||
classCombo.SelectedIndex = -1;
|
||||
_suppress = false;
|
||||
|
||||
_classFilter = null;
|
||||
BuildBrowser();
|
||||
BuildList();
|
||||
}
|
||||
|
||||
private IEnumerable<string> ClassesForCategory(Func<string?, bool>? category) =>
|
||||
@@ -322,87 +323,83 @@ public partial class Depot : UserControl
|
||||
{
|
||||
if (_suppress) return;
|
||||
_classFilter = (classCombo.SelectedItem as ComboBoxItem)?.Tag as string;
|
||||
BuildBrowser();
|
||||
BuildList();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------- vehicle browser
|
||||
|
||||
// Browser groups by the JSON "group" field, rendered as collapsed expanders.
|
||||
// Collapsed groups hold no controls, so the list stays light and smooth.
|
||||
private void BuildBrowser()
|
||||
// Flat list of plain-text vehicle entries (no expanders, like the original
|
||||
// Starter). Empty by default: an entry only appears once a category is chosen
|
||||
// or a search is typed. Picking one drives the mini preview and Add button.
|
||||
private void BuildList()
|
||||
{
|
||||
browserStack.Children.Clear();
|
||||
// reset selection-dependent UI
|
||||
_browserSelected = null;
|
||||
miniPreview.Source = null;
|
||||
addVehicleButton.IsEnabled = false;
|
||||
|
||||
vehicleListBox.Items.Clear();
|
||||
|
||||
string search = searchBox.Text?.Trim() ?? "";
|
||||
bool hasSearch = search.Length > 0;
|
||||
|
||||
var matched = _db.Textures.Where(t => PassesFilters(t, search, hasSearch)).ToList();
|
||||
// nothing selected and nothing searched -> keep the list empty
|
||||
if (_categoryFilter == null && !hasSearch)
|
||||
return;
|
||||
|
||||
// Only auto-open groups when the search is specific. A broad search keeps
|
||||
// groups folded (just headers + counts) so we never realize thousands of
|
||||
// rows / decode thousands of bitmaps at once.
|
||||
bool autoExpand = hasSearch && matched.Count <= AutoExpandLimit;
|
||||
|
||||
// fold by class (the group's mini)
|
||||
var grouped = matched
|
||||
.GroupBy(ClassOf)
|
||||
.OrderBy(g => g.Key, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var group in grouped)
|
||||
{
|
||||
var list = group
|
||||
var matched = _db.Textures
|
||||
.Where(t => PassesFilters(t, search, hasSearch))
|
||||
.OrderBy(t => t.TextureMini ?? t.Skinfile, StringComparer.OrdinalIgnoreCase)
|
||||
.Take(MaxListRows)
|
||||
.ToList();
|
||||
|
||||
string header = string.IsNullOrEmpty(group.Key)
|
||||
? App.Loc["Others"]
|
||||
: group.Key;
|
||||
|
||||
var content = new StackPanel { Spacing = 2 };
|
||||
var expander = new Expander
|
||||
foreach (var texture in matched)
|
||||
{
|
||||
Header = $"{header} ({list.Count})",
|
||||
IsExpanded = autoExpand, // folded by default
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch
|
||||
};
|
||||
expander.Content = content;
|
||||
|
||||
bool populated = false;
|
||||
void Populate()
|
||||
vehicleListBox.Items.Add(new ListBoxItem
|
||||
{
|
||||
if (populated) return;
|
||||
populated = true;
|
||||
|
||||
int shown = Math.Min(list.Count, MaxRowsPerGroup);
|
||||
for (int i = 0; i < shown; i++)
|
||||
content.Children.Add(BuildBrowserItem(list[i]));
|
||||
|
||||
if (list.Count > shown)
|
||||
content.Children.Add(new TextBlock
|
||||
{
|
||||
Text = $"… +{list.Count - shown}",
|
||||
Opacity = 0.6,
|
||||
Margin = new Thickness(6, 2)
|
||||
Content = BrowserLabel(texture, _db.ResolveSet(texture)),
|
||||
Tag = texture
|
||||
});
|
||||
}
|
||||
|
||||
if (expander.IsExpanded)
|
||||
Populate();
|
||||
expander.Expanded += (_, _) => Populate();
|
||||
|
||||
browserStack.Children.Add(expander);
|
||||
}
|
||||
|
||||
if (browserStack.Children.Count == 0)
|
||||
if (vehicleListBox.Items.Count == 0)
|
||||
vehicleListBox.Items.Add(new ListBoxItem
|
||||
{
|
||||
browserStack.Children.Add(new TextBlock
|
||||
{
|
||||
Text = App.Loc["NoVehicles"],
|
||||
Opacity = 0.6,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Margin = new Thickness(4)
|
||||
Content = App.Loc["NoVehicles"],
|
||||
IsEnabled = false
|
||||
});
|
||||
}
|
||||
|
||||
// Selecting an entry shows its mini preview and enables the Add button.
|
||||
private void VehicleListBox_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (vehicleListBox.SelectedItem is ListBoxItem { Tag: VehicleTexture texture })
|
||||
{
|
||||
_browserSelected = texture;
|
||||
miniPreview.Source = GetMiniBitmap(_db.ResolveMiniName(texture), 54);
|
||||
addVehicleButton.IsEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_browserSelected = null;
|
||||
miniPreview.Source = null;
|
||||
addVehicleButton.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Double-click an entry to replace the selected consist vehicle.
|
||||
private void VehicleListBox_OnDoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
|
||||
{
|
||||
if (_browserSelected != null)
|
||||
ReplaceSelected(_browserSelected);
|
||||
}
|
||||
|
||||
// The single Add button inserts the highlighted vehicle after the selected
|
||||
// consist vehicle (or at the end when nothing is selected).
|
||||
private void AddVehicleButton_OnClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
{
|
||||
if (_browserSelected != null)
|
||||
AddTexture(_browserSelected);
|
||||
}
|
||||
|
||||
private bool PassesFilters(VehicleTexture t, string search, bool hasSearch)
|
||||
@@ -428,54 +425,6 @@ public partial class Depot : UserControl
|
||||
|| C(t.Meta?.Vehicle) || C(t.Meta?.Operator) || C(ClassOf(t));
|
||||
}
|
||||
|
||||
private Control BuildBrowserItem(VehicleTexture texture)
|
||||
{
|
||||
// Grid with a star label column keeps the label width constrained so it
|
||||
// wraps instead of overlapping the Add button.
|
||||
var grid = new Grid
|
||||
{
|
||||
Margin = new Thickness(2, 1),
|
||||
ColumnDefinitions = new ColumnDefinitions("Auto,*,Auto")
|
||||
};
|
||||
ToolTip.SetTip(grid, texture.FullPath);
|
||||
|
||||
var thumb = CreateLazyMini(_db.ResolveMiniName(texture), BrowserThumbHeight, 64);
|
||||
thumb.VerticalAlignment = VerticalAlignment.Center;
|
||||
Grid.SetColumn(thumb, 0);
|
||||
|
||||
var set = _db.ResolveSet(texture);
|
||||
var label = new TextBlock
|
||||
{
|
||||
Text = BrowserLabel(texture, set),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
FontSize = 12,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Margin = new Thickness(8, 0, 8, 0)
|
||||
};
|
||||
Grid.SetColumn(label, 1);
|
||||
|
||||
var add = new Button
|
||||
{
|
||||
Content = App.Loc["AddVehicle"],
|
||||
FontSize = 11,
|
||||
Padding = new Thickness(10, 3),
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Cursor = _hand
|
||||
};
|
||||
add.Classes.Add("Flat");
|
||||
ToolTip.SetTip(add, set != null ? App.Loc["TipAddUnit"] : App.Loc["TipAddVehicle"]);
|
||||
add.Click += (_, _) => AddTexture(texture);
|
||||
Grid.SetColumn(add, 2);
|
||||
|
||||
grid.Children.Add(thumb);
|
||||
grid.Children.Add(label);
|
||||
grid.Children.Add(add);
|
||||
|
||||
// double-click the row to replace the selected consist vehicle
|
||||
grid.DoubleTapped += (_, _) => ReplaceSelected(texture);
|
||||
return grid;
|
||||
}
|
||||
|
||||
// Replaces the selected consist unit with this vehicle/unit (keeping its
|
||||
// slot, crew and orientation). Falls back to appending if nothing is selected.
|
||||
private void ReplaceSelected(VehicleTexture texture)
|
||||
@@ -506,29 +455,6 @@ public partial class Depot : UserControl
|
||||
RebuildConsist();
|
||||
}
|
||||
|
||||
// An image that decodes its mini only while it is on screen, and releases it
|
||||
// when scrolled out of view, so an open group never holds every bitmap at once.
|
||||
private Image CreateLazyMini(string? miniName, int height, double width)
|
||||
{
|
||||
var img = new Image { Height = height, Width = width, Stretch = Stretch.Uniform };
|
||||
bool loaded = false;
|
||||
img.EffectiveViewportChanged += (_, e) =>
|
||||
{
|
||||
bool visible = e.EffectiveViewport.Width > 0 && e.EffectiveViewport.Height > 0;
|
||||
if (visible && !loaded)
|
||||
{
|
||||
img.Source = GetMiniBitmap(miniName, height);
|
||||
loaded = true;
|
||||
}
|
||||
else if (!visible && loaded)
|
||||
{
|
||||
img.Source = null;
|
||||
loaded = false;
|
||||
}
|
||||
};
|
||||
return img;
|
||||
}
|
||||
|
||||
private string BrowserLabel(VehicleTexture texture, IReadOnlyList<VehicleTexture>? set)
|
||||
{
|
||||
string name = !string.IsNullOrEmpty(texture.TextureMini)
|
||||
@@ -644,13 +570,13 @@ public partial class Depot : UserControl
|
||||
|
||||
foreach (var v in trainset.Vehicles)
|
||||
{
|
||||
var bmp = GetMiniBitmap(_db.MiniForSkin(v.SkinFile) ?? v.MiniName, 28);
|
||||
var bmp = GetMiniBitmap(_db.MiniForSkin(v.SkinFile) ?? v.MiniName, 20);
|
||||
if (bmp != null)
|
||||
minis.Children.Add(new Image { Source = bmp, Height = 28, Stretch = Stretch.Uniform });
|
||||
minis.Children.Add(new Image { Source = bmp, Height = 20, Stretch = Stretch.Uniform });
|
||||
else
|
||||
minis.Children.Add(new Border
|
||||
{
|
||||
Height = 28, Width = 46, Background = Placeholder, CornerRadius = new CornerRadius(3)
|
||||
Height = 20, Width = 32, Background = Placeholder, CornerRadius = new CornerRadius(3)
|
||||
});
|
||||
|
||||
names.Children.Add(new TextBlock { Text = v.SkinFile, FontSize = 10, Opacity = 0.8 });
|
||||
|
||||
@@ -1,68 +1,161 @@
|
||||
<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:sukiUi="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:starterNg="clr-namespace:StarterNG"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="StarterNG.Views.Scenarios"
|
||||
x:CompileBindings="True">
|
||||
<Grid Margin="16" ColumnSpacing="16" RowSpacing="16">
|
||||
<Grid Margin="0" ColumnSpacing="8" RowSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300" />
|
||||
<ColumnDefinition Width="280" />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="280" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="100" />
|
||||
<RowDefinition Height="40" />
|
||||
<RowDefinition Height="78" />
|
||||
</Grid.RowDefinitions>
|
||||
<sukiUi:GroupBox Grid.RowSpan="2" Header="{Binding [Sceneries], Source={x:Static starterNg:App.Loc}}">
|
||||
<TreeView Name="sceneryList" SelectionChanged="SceneryList_OnSelectionChanged">
|
||||
|
||||
</TreeView>
|
||||
</sukiUi:GroupBox>
|
||||
<sukiUi:GroupBox Grid.Column="1" Header="{Binding [Vehicles], Source={x:Static starterNg:App.Loc}}">
|
||||
<ListBox Name="vehicleList" SelectionChanged="VehicleList_OnSelectionChanged">
|
||||
</ListBox>
|
||||
</sukiUi:GroupBox>
|
||||
<sukiUi:GroupBox Grid.Column="1" Grid.Row="1" Header="{Binding [ScenarioDescription], Source={x:Static starterNg:App.Loc}}">
|
||||
<!-- Scenery tree (left) -->
|
||||
<HeaderedContentControl Grid.Column="0" Grid.Row="0" Theme="{StaticResource GroupBox}"
|
||||
Classes="Compact"
|
||||
Header="{Binding [Sceneries], Source={x:Static starterNg:App.Loc}}">
|
||||
<TreeView Name="sceneryList" SelectionChanged="SceneryList_OnSelectionChanged" />
|
||||
</HeaderedContentControl>
|
||||
|
||||
<!-- Centre: trainsets list (compact, fills) + description/weather/timetable tabs -->
|
||||
<Grid Grid.Column="1" Grid.Row="0" RowSpacing="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="2*" />
|
||||
<RowDefinition Height="3*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<HeaderedContentControl Grid.Row="0" Theme="{StaticResource GroupBox}"
|
||||
Header="{Binding [Vehicles], Source={x:Static starterNg:App.Loc}}">
|
||||
<ListBox Name="vehicleList" Classes="Compact"
|
||||
SelectionChanged="VehicleList_OnSelectionChanged" />
|
||||
</HeaderedContentControl>
|
||||
|
||||
<TabControl Grid.Row="1">
|
||||
<TabItem Header="{Binding [TabDescription], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock TextWrapping="Wrap" Name="missionDescription">
|
||||
</TextBlock>
|
||||
<TextBlock TextWrapping="Wrap" Name="missionDescription" Margin="4" />
|
||||
</ScrollViewer>
|
||||
|
||||
</sukiUi:GroupBox>
|
||||
|
||||
<!-- Scenery image (//$i) shown as a 1:1 square thumbnail -->
|
||||
<Border Grid.Column="2" Grid.Row="0" ClipToBounds="True" VerticalAlignment="Top"
|
||||
Height="{Binding $self.Bounds.Width}">
|
||||
<Image Name="sceneryImage" Stretch="Uniform" />
|
||||
</Border>
|
||||
<!-- Scenery description (//$d), distinct from the scenario/consist description -->
|
||||
<sukiUi:GroupBox Grid.Column="2" Grid.Row="1" Header="{Binding [SceneryDescription], Source={x:Static starterNg:App.Loc}}">
|
||||
</TabItem>
|
||||
<TabItem Header="{Binding [Weather], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock TextWrapping="Wrap" Name="sceneryDescription">
|
||||
</TextBlock>
|
||||
</ScrollViewer>
|
||||
</sukiUi:GroupBox>
|
||||
<StackPanel Name="weatherForm" Margin="8" Spacing="8" IsEnabled="False">
|
||||
<Grid ColumnDefinitions="130,*" RowSpacing="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<sukiUi:GroupBox Header="{Binding [Consist], Source={x:Static starterNg:App.Loc}}" Grid.Row="2" Grid.ColumnSpan="3">
|
||||
<ScrollViewer VerticalAlignment="Bottom"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Disabled">
|
||||
<StackPanel Orientation="Horizontal" Name="consistStack">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"
|
||||
Text="{Binding [Time], Source={x:Static starterNg:App.Loc}}" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Name="weatherTime" Width="90"
|
||||
HorizontalAlignment="Left" Watermark="hh:mm"
|
||||
LostFocus="Weather_OnChanged" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"
|
||||
Text="{Binding [Season], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBox Grid.Row="1" Grid.Column="1" Name="weatherSeason" Width="180"
|
||||
HorizontalAlignment="Left"
|
||||
SelectionChanged="WeatherSeason_OnChanged">
|
||||
<ComboBoxItem Tag="0" Content="{Binding [SeasonNow], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="113" Content="{Binding [SeasonSpring], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="204" Content="{Binding [SeasonSummer], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="297" Content="{Binding [SeasonAutumn], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="15" Content="{Binding [SeasonWinter], Source={x:Static starterNg:App.Loc}}" />
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center"
|
||||
Text="{Binding [Day], Source={x:Static starterNg:App.Loc}}" />
|
||||
<NumericUpDown Grid.Row="2" Grid.Column="1" Name="weatherDay" Width="140"
|
||||
HorizontalAlignment="Left" Minimum="0" Maximum="366"
|
||||
Increment="1" FormatString="0"
|
||||
ValueChanged="WeatherDay_OnChanged" />
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center"
|
||||
Text="{Binding [Temperature], Source={x:Static starterNg:App.Loc}}" />
|
||||
<StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal" Spacing="8">
|
||||
<Slider Name="weatherTemp" Minimum="-20" Maximum="40" Width="220"
|
||||
TickFrequency="1" IsSnapToTickEnabled="True"
|
||||
ValueChanged="Weather_OnSliderChanged" />
|
||||
<TextBlock Name="weatherTempLabel" VerticalAlignment="Center" MinWidth="48" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" VerticalAlignment="Center"
|
||||
Text="{Binding [Visibility], Source={x:Static starterNg:App.Loc}}" />
|
||||
<StackPanel Grid.Row="4" Grid.Column="1" Orientation="Horizontal" Spacing="8">
|
||||
<Slider Name="weatherFog" Minimum="50" Maximum="2500" Width="220"
|
||||
TickFrequency="50" IsSnapToTickEnabled="True"
|
||||
ValueChanged="Weather_OnSliderChanged" />
|
||||
<TextBlock Name="weatherFogLabel" VerticalAlignment="Center" MinWidth="56" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Row="5" Grid.Column="0" VerticalAlignment="Center"
|
||||
Text="{Binding [Overcast], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBox Grid.Row="5" Grid.Column="1" Name="weatherOvercast" Width="220"
|
||||
HorizontalAlignment="Left"
|
||||
SelectionChanged="Weather_OnSelectionChanged">
|
||||
<ComboBoxItem Tag="-1.5" Content="{Binding [OvercastClear], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="0" Content="{Binding [OvercastFair], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="0.3" Content="{Binding [OvercastLight], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="0.7" Content="{Binding [OvercastModerate], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="1" Content="{Binding [OvercastHeavy], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="1.1" Content="{Binding [OvercastCloudy], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="1.5" Content="{Binding [OvercastStorm], Source={x:Static starterNg:App.Loc}}" />
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Name="weatherHint" Opacity="0.6" FontSize="11" TextWrapping="Wrap"
|
||||
Text="{Binding [WeatherHint], Source={x:Static starterNg:App.Loc}}" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</sukiUi:GroupBox>
|
||||
</TabItem>
|
||||
<TabItem Header="{Binding [Timetable], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
|
||||
<TextBlock Name="timetableContent" Margin="4" FontFamily="Consolas, monospace"
|
||||
FontSize="12" />
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Grid>
|
||||
|
||||
<Button Grid.Column="0" Height="36" Classes="Flat"
|
||||
VerticalAlignment="Bottom" Grid.Row="3" Grid.ColumnSpan="3"
|
||||
Click="StartButton_OnClick"
|
||||
Content="{Binding [Start], Source={x:Static starterNg:App.Loc}}">
|
||||
<!-- Right: scenery image (//$i) + scenery description (//$d) -->
|
||||
<Grid Grid.Column="2" Grid.Row="0" RowSpacing="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
</Button>
|
||||
<Border Grid.Row="0" ClipToBounds="True" VerticalAlignment="Top"
|
||||
Background="{StaticResource CarbonPanelBg}"
|
||||
BorderBrush="{StaticResource CarbonBorderLight}" BorderThickness="1"
|
||||
Height="{Binding $self.Bounds.Width}">
|
||||
<Image Name="sceneryImage" Stretch="Uniform" Margin="2" />
|
||||
</Border>
|
||||
|
||||
<HeaderedContentControl Grid.Row="1" Theme="{StaticResource GroupBox}"
|
||||
Header="{Binding [SceneryDescription], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock TextWrapping="Wrap" Name="sceneryDescription" />
|
||||
</ScrollViewer>
|
||||
</HeaderedContentControl>
|
||||
</Grid>
|
||||
|
||||
<!-- Consist preview (full-width bottom, horizontally scrollable) -->
|
||||
<HeaderedContentControl Grid.Row="1" Grid.ColumnSpan="3" Theme="{StaticResource GroupBox}"
|
||||
Header="{Binding [Consist], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Name="consistStack" />
|
||||
</ScrollViewer>
|
||||
</HeaderedContentControl>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -16,6 +17,11 @@ public partial class Scenarios : UserControl
|
||||
{
|
||||
public List<Scenery> Sceneries;
|
||||
|
||||
// scenery whose weather the form is currently editing
|
||||
private Scenery? _currentScenery;
|
||||
// suppresses weather change handlers while the form is being populated
|
||||
private bool _loadingWeather;
|
||||
|
||||
public Scenarios()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -76,6 +82,11 @@ public partial class Scenarios : UserControl
|
||||
|
||||
// scenery-level info (distinct from the per-consist scenario description)
|
||||
ShowSceneryInfo(selectedScn);
|
||||
ShowWeather(selectedScn);
|
||||
|
||||
// no consist chosen yet for this scenery
|
||||
missionDescription.Text = "";
|
||||
timetableContent.Text = App.Loc["NoTimetable"];
|
||||
|
||||
// add all trainsets to list
|
||||
for (int i = 0; i < selectedScn.Trainsets.Count; i++)
|
||||
@@ -136,6 +147,135 @@ public partial class Scenarios : UserControl
|
||||
AppState.Instance.CurrentTrainset = selectedTrainset;
|
||||
|
||||
ShowConsist(selectedTrainset);
|
||||
ShowTimetable(selectedScn, selectedTrainset);
|
||||
}
|
||||
|
||||
// Loads the scenery's weather into the editable form (Weather tab). Editing a
|
||||
// control writes the value back onto the scenery and marks it dirty, so the
|
||||
// change is injected into the exported .scn's config block on launch.
|
||||
private void ShowWeather(Scenery scenery)
|
||||
{
|
||||
_currentScenery = scenery;
|
||||
_loadingWeather = true;
|
||||
try
|
||||
{
|
||||
weatherForm.IsEnabled = true;
|
||||
weatherTime.Text = scenery.WeatherTime;
|
||||
weatherDay.Value = scenery.Day;
|
||||
weatherTemp.Value = scenery.Temperature;
|
||||
weatherFog.Value = System.Math.Clamp(scenery.FogEnd, 50, 2500);
|
||||
SelectByTag(weatherSeason, scenery.Day.ToString());
|
||||
SelectByTag(weatherOvercast, scenery.Overcast.ToString(CultureInfo.InvariantCulture));
|
||||
UpdateWeatherLabels();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_loadingWeather = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads the form back into the current scenery and flags it for export rewrite.
|
||||
private void CaptureWeather()
|
||||
{
|
||||
if (_loadingWeather || _currentScenery is null)
|
||||
return;
|
||||
|
||||
var scn = _currentScenery;
|
||||
if (!string.IsNullOrWhiteSpace(weatherTime.Text))
|
||||
scn.WeatherTime = weatherTime.Text!.Trim();
|
||||
scn.Day = (int)(weatherDay.Value ?? 0);
|
||||
scn.Temperature = weatherTemp.Value;
|
||||
scn.FogEnd = (int)weatherFog.Value;
|
||||
if ((weatherOvercast.SelectedItem as ComboBoxItem)?.Tag is string ov &&
|
||||
double.TryParse(ov, System.Globalization.NumberStyles.Float, CultureInfo.InvariantCulture, out double oc))
|
||||
scn.Overcast = oc;
|
||||
|
||||
scn.WeatherDirty = true;
|
||||
UpdateWeatherLabels();
|
||||
}
|
||||
|
||||
private void UpdateWeatherLabels()
|
||||
{
|
||||
weatherTempLabel.Text = $"{(int)weatherTemp.Value} °C";
|
||||
weatherFogLabel.Text = $"{(int)weatherFog.Value} m";
|
||||
}
|
||||
|
||||
private static void SelectByTag(ComboBox combo, string tag)
|
||||
{
|
||||
foreach (var obj in combo.Items)
|
||||
if (obj is ComboBoxItem item && (item.Tag as string) == tag)
|
||||
{
|
||||
combo.SelectedItem = item;
|
||||
return;
|
||||
}
|
||||
combo.SelectedItem = null;
|
||||
}
|
||||
|
||||
// --- weather form event handlers ---
|
||||
private void Weather_OnChanged(object? sender, RoutedEventArgs e) => CaptureWeather();
|
||||
|
||||
private void Weather_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) => CaptureWeather();
|
||||
|
||||
private void Weather_OnSliderChanged(object? sender, Avalonia.Controls.Primitives.RangeBaseValueChangedEventArgs e) => CaptureWeather();
|
||||
|
||||
private void WeatherDay_OnChanged(object? sender, NumericUpDownValueChangedEventArgs e) => CaptureWeather();
|
||||
|
||||
// Picking a season presets the day-of-year, then captures the form.
|
||||
private void WeatherSeason_OnChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (_loadingWeather)
|
||||
return;
|
||||
if ((weatherSeason.SelectedItem as ComboBoxItem)?.Tag is string tag &&
|
||||
int.TryParse(tag, out int day))
|
||||
{
|
||||
_loadingWeather = true;
|
||||
weatherDay.Value = day;
|
||||
_loadingWeather = false;
|
||||
}
|
||||
CaptureWeather();
|
||||
}
|
||||
|
||||
// Reads and shows the timetable file referenced by the trainset (its first
|
||||
// "trainset" token is the timetable name). Shows a note when none is set or
|
||||
// the file cannot be found.
|
||||
private void ShowTimetable(Scenery scenery, Trainset trainset)
|
||||
{
|
||||
string? path = ResolveTimetablePath(scenery, trainset.Name);
|
||||
if (path is null)
|
||||
{
|
||||
timetableContent.Text = App.Loc["NoTimetable"];
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
timetableContent.Text = File.ReadAllText(path, Encoding.GetEncoding(1250));
|
||||
}
|
||||
catch
|
||||
{
|
||||
timetableContent.Text = App.Loc["NoTimetable"];
|
||||
}
|
||||
}
|
||||
|
||||
// Probes the usual locations for a timetable file named after the trainset.
|
||||
private static string? ResolveTimetablePath(Scenery scenery, string? name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name) ||
|
||||
name.Equals("none", System.StringComparison.OrdinalIgnoreCase))
|
||||
return null;
|
||||
|
||||
string scnDir = Path.GetDirectoryName(scenery.Path) ?? "."; // scenery/
|
||||
string root = Path.GetDirectoryName(scnDir) ?? "."; // MaSzyna root
|
||||
|
||||
var candidates = new[]
|
||||
{
|
||||
Path.Combine(root, "timetables", name + ".txt"),
|
||||
Path.Combine(scnDir, name + ".txt"),
|
||||
Path.Combine(root, "scenario", name + ".txt"),
|
||||
Path.Combine(root, "timetables", name),
|
||||
Path.Combine(scnDir, name),
|
||||
};
|
||||
return candidates.FirstOrDefault(File.Exists);
|
||||
}
|
||||
|
||||
// Renders the consist preview for a trainset (re-reads its current vehicles
|
||||
@@ -156,7 +296,7 @@ public partial class Scenarios : UserControl
|
||||
consistStack.Children.Add(new Image
|
||||
{
|
||||
Source = new Bitmap(path),
|
||||
Height = 45,
|
||||
Height = 32,
|
||||
Stretch = Avalonia.Media.Stretch.Uniform,
|
||||
Margin = new Thickness(0)
|
||||
});
|
||||
@@ -171,49 +311,4 @@ public partial class Scenarios : UserControl
|
||||
if (AppState.Instance.CurrentTrainset is { } trainset)
|
||||
ShowConsist(trainset);
|
||||
}
|
||||
|
||||
// Exports the (possibly depot-modified) scenery to a $-prefixed copy and
|
||||
// launches the game on it with the selected consist's driven vehicle.
|
||||
private void StartButton_OnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var scenery = AppState.Instance.CurrentScenery;
|
||||
if (scenery is null)
|
||||
return;
|
||||
var trainset = AppState.Instance.CurrentTrainset;
|
||||
|
||||
// write scenery/$<name>.scn with the replaced trainsets
|
||||
string dir = System.IO.Path.GetDirectoryName(scenery.Path) ?? "scenery";
|
||||
string exportName = "$" + System.IO.Path.GetFileName(scenery.Path);
|
||||
string exportPath = System.IO.Path.Combine(dir, exportName);
|
||||
try
|
||||
{
|
||||
File.WriteAllText(exportPath, scenery.BuildExportContent(), Encoding.GetEncoding(1250));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return; // couldn't write the scenery file
|
||||
}
|
||||
|
||||
// the player's vehicle is the node name of the driven car in the consist
|
||||
string? vehicle = trainset?.Vehicles
|
||||
.FirstOrDefault(v => v.DriverType is eDriverType.Headdriver or eDriverType.Reardriver)?.Name
|
||||
?? trainset?.Vehicles.FirstOrDefault()?.Name;
|
||||
|
||||
// make sure the latest settings are on disk before the sim reads them
|
||||
StarterNG.Classes.Settings.Instance.CaptureAndSave();
|
||||
|
||||
// launch the game: -s $<name>.scn -v <vehicle>
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(StarterNG.Classes.Settings.Instance.ExecutablePath)
|
||||
{
|
||||
Arguments = $"-s {exportName} -v {vehicle}",
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// executable not found / not configured yet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
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:suki="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
||||
xmlns:objectModel="clr-namespace:System.Collections.ObjectModel;assembly=System.ObjectModel"
|
||||
xmlns:starterNg="clr-namespace:StarterNG"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="StarterNG.Views.Settings"
|
||||
x:CompileBindings="True">
|
||||
<DockPanel>
|
||||
<!-- Save / reset bar -->
|
||||
<Border DockPanel.Dock="Bottom" Padding="16,10" Background="#11000000">
|
||||
<Border DockPanel.Dock="Bottom" Padding="16,10" Background="{StaticResource CarbonPanelBg}"
|
||||
BorderBrush="{StaticResource CarbonBorderLight}" BorderThickness="0,1,0,0">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12" HorizontalAlignment="Right">
|
||||
<TextBlock x:Name="SaveStatus" VerticalAlignment="Center" Opacity="0.8" Text="" />
|
||||
<Button x:Name="ResetButton" Classes="Flat" Click="ResetButton_OnClick"
|
||||
@@ -20,93 +19,89 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<suki:SettingsLayout>
|
||||
<suki:SettingsLayout.Items>
|
||||
<objectModel:ObservableCollection x:TypeArguments="suki:SettingsLayoutItem">
|
||||
<suki:SettingsLayoutItem Header="{Binding [General], Source={x:Static starterNg:App.Loc}}">
|
||||
<suki:SettingsLayoutItem.Content>
|
||||
<StackPanel Spacing="8" Orientation="Vertical">
|
||||
<TabControl TabStripPlacement="Left">
|
||||
<!-- General -->
|
||||
<TabItem Header="{Binding [General], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="8" Orientation="Vertical" Margin="12">
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<Label Content="{Binding [Language], Source={x:Static starterNg:App.Loc}}" VerticalAlignment="Center" FontSize="14"/>
|
||||
<Label Content="{Binding [Language], Source={x:Static starterNg:App.Loc}}" VerticalAlignment="Center" FontSize="14" />
|
||||
<ComboBox x:Name="ChangeLanguageCb" MinWidth="350" MaxWidth="350" SelectedIndex="1" SelectionChanged="LanguageComboBox_OnSelectionChanged">
|
||||
<ComboBoxItem>Polski</ComboBoxItem>
|
||||
<ComboBoxItem>English</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox x:Name="FullscreenCb" Content="{Binding [Fullscreen], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="FullscreenCb" Content="{Binding [Fullscreen], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="PauseInactiveCb" Content="{Binding [PauseSimInactive], Source={x:Static starterNg:App.Loc}}" />
|
||||
|
||||
<CheckBox x:Name="PauseStartCb" Content="{Binding [PauseSimStart], Source={x:Static starterNg:App.Loc}}" />
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [CursorSensitivity], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [CursorSensitivity], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="CursorSensitivitySlider" Minimum="1" Maximum="5" TickFrequency="1" MinWidth="200" MaxWidth="200"
|
||||
IsSnapToTickEnabled="True" Value="3">
|
||||
</Slider>
|
||||
IsSnapToTickEnabled="True" Value="3" />
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox x:Name="MouseHorInvertCb" Content="{Binding [MouseHorInvert], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<CheckBox x:Name="MouseHorInvertCb" Content="{Binding [MouseHorInvert], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="MouseVertInvertCb" Content="{Binding [MouseVertInvert], Source={x:Static starterNg:App.Loc}}" />
|
||||
</StackPanel>
|
||||
</suki:SettingsLayoutItem.Content>
|
||||
</suki:SettingsLayoutItem>
|
||||
<suki:SettingsLayoutItem Header="{Binding [Communication], Source={x:Static starterNg:App.Loc}}">
|
||||
<suki:SettingsLayoutItem.Content>
|
||||
<StackPanel Spacing="8" Orientation="Vertical">
|
||||
<CheckBox x:Name="GamepadIgnoreCb" Content="{Binding [GamepadSignalIgnore], Source={x:Static starterNg:App.Loc}}"/>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<!-- Communication -->
|
||||
<TabItem Header="{Binding [Communication], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="8" Orientation="Vertical" Margin="12">
|
||||
<CheckBox x:Name="GamepadIgnoreCb" Content="{Binding [GamepadSignalIgnore], Source={x:Static starterNg:App.Loc}}" />
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [Feedback], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [Feedback], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBox x:Name="FeedbackCb" MinWidth="350" MaxWidth="350" SelectedIndex="0">
|
||||
<ComboBoxItem Tag="0" Content="{Binding [FeedbackOff], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Tag="1" Content="{Binding [FeedbackCASHP], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Tag="2" Content="{Binding [FeedbackCA], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Tag="3" Content="{Binding [FeedbackLPT], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Tag="4" Content="{Binding [FeedbackPoKeys], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Tag="5" Content="{Binding [FeedbackSP], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Tag="0" Content="{Binding [FeedbackOff], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="1" Content="{Binding [FeedbackCASHP], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="2" Content="{Binding [FeedbackCA], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="3" Content="{Binding [FeedbackLPT], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="4" Content="{Binding [FeedbackPoKeys], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Tag="5" Content="{Binding [FeedbackSP], Source={x:Static starterNg:App.Loc}}" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</suki:SettingsLayoutItem.Content>
|
||||
</suki:SettingsLayoutItem>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<suki:SettingsLayoutItem Header="{Binding [Others], Source={x:Static starterNg:App.Loc}}">
|
||||
<suki:SettingsLayoutItem.Content>
|
||||
<StackPanel Spacing="8" Orientation="Vertical">
|
||||
<!-- Others -->
|
||||
<TabItem Header="{Binding [Others], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="8" Orientation="Vertical" Margin="12">
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [SelectEXE], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [SelectEXE], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBox x:Name="SelectExeCb" MinWidth="350" MaxWidth="350" SelectedIndex="0">
|
||||
<ComboBoxItem Content="{Binding [SelectEXEAuto], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [SelectEXEAuto], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem>eu07.exe</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox x:Name="DebugModeCb" Content="{Binding [DebugMode], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="VirtualShuntingCb" Content="{Binding [VirtualShunting], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<CheckBox x:Name="DebugModeCb" Content="{Binding [DebugMode], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="VirtualShuntingCb" Content="{Binding [VirtualShunting], Source={x:Static starterNg:App.Loc}}" />
|
||||
</StackPanel>
|
||||
</suki:SettingsLayoutItem.Content>
|
||||
</suki:SettingsLayoutItem>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<suki:SettingsLayoutItem Header="{Binding [Graphic], Source={x:Static starterNg:App.Loc}}">
|
||||
<suki:SettingsLayoutItem.Content>
|
||||
<StackPanel Spacing="8" Orientation="Vertical">
|
||||
<!-- Graphic -->
|
||||
<TabItem Header="{Binding [Graphic], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="8" Orientation="Vertical" Margin="12">
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [RenderEngine], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [RenderEngine], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBox x:Name="RenderEngineCb" MinWidth="150" MaxWidth="150" SelectedIndex="0">
|
||||
<ComboBoxItem Content="{Binding [REFull], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [REOld], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [RESimpShader], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [RESimp], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [REOff], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [REExperimental], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [REFull], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Content="{Binding [REOld], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Content="{Binding [RESimpShader], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Content="{Binding [RESimp], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Content="{Binding [REOff], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Content="{Binding [REExperimental], Source={x:Static starterNg:App.Loc}}" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [Resolution], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding [Resolution], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBox x:Name="ResolutionCb" MinWidth="150" MaxWidth="150" SelectedIndex="0">
|
||||
<ComboBoxItem>3840x2160</ComboBoxItem>
|
||||
<ComboBoxItem>2560x1440</ComboBoxItem>
|
||||
@@ -117,283 +112,188 @@
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [BufforResolution], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [BufforResolution], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider Name="bufferScale" Minimum="50" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="200" TickFrequency="1" IsSnapToTickEnabled="True"
|
||||
Value="100" />
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{Binding #bufferScale.Value, StringFormat='{}{0}%'}" />
|
||||
MinWidth="250" Maximum="200" TickFrequency="1" IsSnapToTickEnabled="True" Value="100" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{Binding #bufferScale.Value, StringFormat='{}{0}%'}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [MaxTexResolution], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider Name="textureResolutionSlider" Minimum="9" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="14"
|
||||
ValueChanged="TextureResolutionSlider_OnValueChanged" TickFrequency="1"
|
||||
IsSnapToTickEnabled="True" Value="12" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [MaxTexResolution], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider Name="textureResolutionSlider" Minimum="9" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="14" ValueChanged="TextureResolutionSlider_OnValueChanged"
|
||||
TickFrequency="1" IsSnapToTickEnabled="True" Value="12" />
|
||||
<TextBlock Name="texResolution" VerticalAlignment="Center" Text="4096" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [MaxTexCabResolution], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider Name="cabTextureResolutionSlider" Minimum="9" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="14"
|
||||
ValueChanged="CabTextureResolutionSlider_OnValueChanged" TickFrequency="1"
|
||||
IsSnapToTickEnabled="True" Value="12" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [MaxTexCabResolution], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider Name="cabTextureResolutionSlider" Minimum="9" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="14" ValueChanged="CabTextureResolutionSlider_OnValueChanged"
|
||||
TickFrequency="1" IsSnapToTickEnabled="True" Value="12" />
|
||||
<TextBlock Name="cabTexResolution" VerticalAlignment="Center" Text="4096" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [TexFilteringQuality], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="TexFilteringSlider" Minimum="1" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="5"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True" Value="4" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [TexFilteringQuality], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="TexFilteringSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="5" TickFrequency="1" IsSnapToTickEnabled="True" Value="4" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [Multisampling], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="MultisamplingSlider" Minimum="1" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="4"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True" Value="3" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [Multisampling], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="MultisamplingSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="4" TickFrequency="1" IsSnapToTickEnabled="True" Value="3" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [RenderRange], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="RenderRangeSlider" Minimum="1" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [RenderRange], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="RenderRangeSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="3" Value="1" />
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox x:Name="VSyncCb" Content="{Binding [VSync], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="SmokeDisplayCb" Content="{Binding [SmokeDisplay], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="VSyncCb" Content="{Binding [VSync], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="SmokeDisplayCb" Content="{Binding [SmokeDisplay], Source={x:Static starterNg:App.Loc}}" />
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [SmokeParticlesMultiplier], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="SmokeParticlesSlider" Minimum="1" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="4"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True" Value="2" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [SmokeParticlesMultiplier], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="SmokeParticlesSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="4" TickFrequency="1" IsSnapToTickEnabled="True" Value="2" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [Postprocessing], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [Postprocessing], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBox x:Name="PostprocessingCb" MinWidth="150" MaxWidth="150" SelectedIndex="0">
|
||||
<ComboBoxItem Content="{Binding [PPReinhard], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [PPACES], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [PPReinhard], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Content="{Binding [PPACES], Source={x:Static starterNg:App.Loc}}" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox x:Name="ChromaticAberrationCb" Content="{Binding [ChromaticAberration], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="MotionBlurCb" Content="{Binding [MotionBlur], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="AdditionalShadersCb" Content="{Binding [AdditionalShadersEffects], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="ReflectionsCubeMapCb" Content="{Binding [ReflectionsCubeMap], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="RenderVBOCb" Content="{Binding [RenderVBO], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="RenderShadowsCb" Content="{Binding [RenderShadows], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="ChromaticAberrationCb" Content="{Binding [ChromaticAberration], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="MotionBlurCb" Content="{Binding [MotionBlur], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="AdditionalShadersCb" Content="{Binding [AdditionalShadersEffects], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="ReflectionsCubeMapCb" Content="{Binding [ReflectionsCubeMap], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="RenderVBOCb" Content="{Binding [RenderVBO], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="RenderShadowsCb" Content="{Binding [RenderShadows], Source={x:Static starterNg:App.Loc}}" />
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ReflectionsFramerate], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider Name="reflectionsFramerate" Minimum="5" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="120"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True" Value="30" />
|
||||
<Label VerticalAlignment="Center" FontSize="14"
|
||||
Content="{Binding #reflectionsFramerate.Value, StringFormat='{}{0} FPS'}" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ReflectionsFramerate], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider Name="reflectionsFramerate" Minimum="5" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="120" TickFrequency="5" IsSnapToTickEnabled="True" Value="30" />
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding #reflectionsFramerate.Value, StringFormat='{}{0} FPS'}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ShadowsResolution], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider Name="shaderResolutionSlider" Minimum="9" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="14"
|
||||
ValueChanged="shaderResolutionSlider_OnValueChanged" TickFrequency="1"
|
||||
IsSnapToTickEnabled="True" Value="12" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ShadowsResolution], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider Name="shaderResolutionSlider" Minimum="9" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="14" ValueChanged="shaderResolutionSlider_OnValueChanged"
|
||||
TickFrequency="1" IsSnapToTickEnabled="True" Value="12" />
|
||||
<TextBlock Name="shaderResolution" VerticalAlignment="Center" Text="4096 px" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ShaderRange], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider Name="shaderRange" Minimum="25" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="400"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True" Value="150" />
|
||||
<Label VerticalAlignment="Center" FontSize="14"
|
||||
Content="{Binding #shaderRange.Value, StringFormat='{}{0} m'}" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ShaderRange], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider Name="shaderRange" Minimum="25" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="400" TickFrequency="5" IsSnapToTickEnabled="True" Value="150" />
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding #shaderRange.Value, StringFormat='{}{0} m'}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [CabShadowsRange], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider Name="cabShaderSourceRange" Minimum="0" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100"
|
||||
TickFrequency="10"
|
||||
IsSnapToTickEnabled="True" Value="30" />
|
||||
<Label VerticalAlignment="Center" FontSize="14"
|
||||
Content="{Binding #cabShaderSourceRange.Value, StringFormat='{}{0} m'}" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [CabShadowsRange], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider Name="cabShaderSourceRange" Minimum="0" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100" TickFrequency="10" IsSnapToTickEnabled="True" Value="30" />
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding #cabShaderSourceRange.Value, StringFormat='{}{0} m'}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ShadowDisplay], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ShadowDisplay], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBox x:Name="ShadowDisplayCb" MinWidth="150" MaxWidth="150" SelectedIndex="0">
|
||||
<ComboBoxItem Content="{Binding [CSROnlyImportant], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [CSRLimited], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [CSRAll], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [CSROnlyImportant], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Content="{Binding [CSRLimited], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Content="{Binding [CSRAll], Source={x:Static starterNg:App.Loc}}" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ReflectionsDetails], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ReflectionsDetails], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBox x:Name="ReflectionsDetailsCb" MinWidth="350" MaxWidth="350" SelectedIndex="0">
|
||||
<ComboBoxItem Content="{Binding [RDTerrain], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [RDTerrainModels], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [RDTerrainModelsVehicles], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<ComboBoxItem Content="{Binding [RDTerrain], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Content="{Binding [RDTerrainModels], Source={x:Static starterNg:App.Loc}}" />
|
||||
<ComboBoxItem Content="{Binding [RDTerrainModelsVehicles], Source={x:Static starterNg:App.Loc}}" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ShadowsCabRange], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider Name="fovSlider" Minimum="5" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="110"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True" Value="30" />
|
||||
<Label VerticalAlignment="Center" FontSize="14"
|
||||
Content="{Binding #fovSlider.Value, StringFormat='{}{0} stopni'}" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [ShadowsCabRange], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider Name="fovSlider" Minimum="5" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="110" TickFrequency="5" IsSnapToTickEnabled="True" Value="30" />
|
||||
<Label VerticalAlignment="Center" FontSize="14" Content="{Binding #fovSlider.Value, StringFormat='{}{0} stopni'}" />
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox x:Name="RenderScreensCb" Content="{Binding [RenderScreens], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<CheckBox x:Name="RenderScreensThreadCb" Content="{Binding [RenderScreensThread], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="RenderScreensCb" Content="{Binding [RenderScreens], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="RenderScreensThreadCb" Content="{Binding [RenderScreensThread], Source={x:Static starterNg:App.Loc}}" />
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [RenderScreensFramerate], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="RenderScreensFramerateSlider" Minimum="1" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="5"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True" Value="5" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [RenderScreensFramerate], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="RenderScreensFramerateSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="5" TickFrequency="1" IsSnapToTickEnabled="True" Value="5" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</suki:SettingsLayoutItem.Content>
|
||||
</suki:SettingsLayoutItem>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<suki:SettingsLayoutItem Header="{Binding [Physics], Source={x:Static starterNg:App.Loc}}">
|
||||
<suki:SettingsLayoutItem.Content>
|
||||
<StackPanel Spacing="8" Orientation="Vertical">
|
||||
<!-- Physics -->
|
||||
<TabItem Header="{Binding [Physics], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="8" Orientation="Vertical" Margin="12">
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [TrackCurvesQuality], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="TrackCurvesSlider" Minimum="1" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="4"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True" Value="4" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [TrackCurvesQuality], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="TrackCurvesSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="4" TickFrequency="1" IsSnapToTickEnabled="True" Value="4" />
|
||||
</StackPanel>
|
||||
|
||||
<CheckBox x:Name="PhysicsAccuracyCb" Content="{Binding [PhysicsAccuracyIncreased], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="PantographBreakCb" Content="{Binding [BreakingPantographPossibility], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="OverheadOnlyCb" Content="{Binding [PowerOverheadLineOnly], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="SpeedometerTapesCb" Content="{Binding [SaveSpeedometerTapes], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="SimLogsCb" Content="{Binding [SaveSimLogs], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="KeepLogsCb" Content="{Binding [KeepPrevLogs], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="DisplaySimulationCb" Content="{Binding [DisplaySimulation], Source={x:Static starterNg:App.Loc}}"/>
|
||||
|
||||
<CheckBox x:Name="CrashDamageCb" Content="{Binding [CrashDamage], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<CheckBox x:Name="PhysicsAccuracyCb" Content="{Binding [PhysicsAccuracyIncreased], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="PantographBreakCb" Content="{Binding [BreakingPantographPossibility], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="OverheadOnlyCb" Content="{Binding [PowerOverheadLineOnly], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="SpeedometerTapesCb" Content="{Binding [SaveSpeedometerTapes], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="SimLogsCb" Content="{Binding [SaveSimLogs], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="KeepLogsCb" Content="{Binding [KeepPrevLogs], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="DisplaySimulationCb" Content="{Binding [DisplaySimulation], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="CrashDamageCb" Content="{Binding [CrashDamage], Source={x:Static starterNg:App.Loc}}" />
|
||||
</StackPanel>
|
||||
</suki:SettingsLayoutItem.Content>
|
||||
</suki:SettingsLayoutItem>
|
||||
|
||||
<suki:SettingsLayoutItem Header="{Binding [Sounds], Source={x:Static starterNg:App.Loc}}">
|
||||
<suki:SettingsLayoutItem.Content>
|
||||
<StackPanel Spacing="8" Orientation="Vertical">
|
||||
<CheckBox x:Name="EnableSoundsCb" Content="{Binding [EnableSounds], Source={x:Static starterNg:App.Loc}}"/>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<!-- Sounds -->
|
||||
<TabItem Header="{Binding [Sounds], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="8" Orientation="Vertical" Margin="12">
|
||||
<CheckBox x:Name="EnableSoundsCb" Content="{Binding [EnableSounds], Source={x:Static starterNg:App.Loc}}" />
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [Volume], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="VolumeSlider" Minimum="1" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True" Value="100" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [Volume], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="VolumeSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100" TickFrequency="5" IsSnapToTickEnabled="True" Value="100" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [RadiophoneVolume], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="RadioVolumeSlider" Minimum="1" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True" Value="100" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [RadiophoneVolume], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="RadioVolumeSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100" TickFrequency="5" IsSnapToTickEnabled="True" Value="100" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [VehiclesVolume], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="VehiclesVolumeSlider" Minimum="1" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True" Value="100" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [VehiclesVolume], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="VehiclesVolumeSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100" TickFrequency="5" IsSnapToTickEnabled="True" Value="100" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [PositionedSoundsVolume], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="PositionalVolumeSlider" Minimum="1" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True" Value="100" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [PositionedSoundsVolume], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="PositionalVolumeSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100" TickFrequency="5" IsSnapToTickEnabled="True" Value="100" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [AmbientVolume], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="AmbientVolumeSlider" Minimum="1" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True" Value="100" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [AmbientVolume], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="AmbientVolumeSlider" Minimum="1" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100" TickFrequency="5" IsSnapToTickEnabled="True" Value="100" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [VolumeDuringPause], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<Slider x:Name="PauseVolumeSlider" Minimum="0" MaxWidth="400"
|
||||
HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100"
|
||||
TickFrequency="5"
|
||||
IsSnapToTickEnabled="True" Value="0" />
|
||||
<TextBlock VerticalAlignment="Center" FontSize="14" Text="{Binding [VolumeDuringPause], Source={x:Static starterNg:App.Loc}}" />
|
||||
<Slider x:Name="PauseVolumeSlider" Minimum="0" MaxWidth="400" HorizontalAlignment="Left"
|
||||
MinWidth="250" Maximum="100" TickFrequency="5" IsSnapToTickEnabled="True" Value="0" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</suki:SettingsLayoutItem.Content>
|
||||
</suki:SettingsLayoutItem>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<suki:SettingsLayoutItem Header="{Binding [Starter], Source={x:Static starterNg:App.Loc}}">
|
||||
<suki:SettingsLayoutItem.Content>
|
||||
<StackPanel Spacing="8" Orientation="Vertical">
|
||||
<CheckBox x:Name="AutoCloseStarterCb" Content="{Binding [AutoCloseStarter], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<CheckBox x:Name="LargeThumbnailsCb" Content="{Binding [LargeThumbnails], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<CheckBox x:Name="AutoExpandTreeCb" Content="{Binding [AutoExpandSceneryTree], Source={x:Static starterNg:App.Loc}}"/>
|
||||
<!-- Starter -->
|
||||
<TabItem Header="{Binding [Starter], Source={x:Static starterNg:App.Loc}}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Spacing="8" Orientation="Vertical" Margin="12">
|
||||
<CheckBox x:Name="AutoCloseStarterCb" Content="{Binding [AutoCloseStarter], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="LargeThumbnailsCb" Content="{Binding [LargeThumbnails], Source={x:Static starterNg:App.Loc}}" />
|
||||
<CheckBox x:Name="AutoExpandTreeCb" Content="{Binding [AutoExpandSceneryTree], Source={x:Static starterNg:App.Loc}}" />
|
||||
</StackPanel>
|
||||
</suki:SettingsLayoutItem.Content>
|
||||
</suki:SettingsLayoutItem>
|
||||
</objectModel:ObservableCollection>
|
||||
</suki:SettingsLayout.Items>
|
||||
</suki:SettingsLayout>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -8,12 +8,40 @@
|
||||
<String key="NavDepot">Depot</String>
|
||||
<String key="NavSettings">Settings</String>
|
||||
<String key="NavControls">Controls</String>
|
||||
<String key="Help">Help</String>
|
||||
<String key="Version">Version:</String>
|
||||
|
||||
<!-- General view -->
|
||||
<String key="Sceneries">Sceneries</String>
|
||||
<String key="Vehicles">Vehicles</String>
|
||||
<String key="ScenarioDescription">Scenario description</String>
|
||||
<String key="SceneryDescription">Scenery description</String>
|
||||
<String key="TabDescription">Description</String>
|
||||
<String key="Weather">Weather</String>
|
||||
<String key="Timetable">Timetable</String>
|
||||
<String key="NoWeatherData">No weather data in the scenery.</String>
|
||||
<String key="NoTimetable">No timetable for the selected consist.</String>
|
||||
<String key="Time">Time</String>
|
||||
<String key="Day">Day of year</String>
|
||||
<String key="Sky">Sky</String>
|
||||
<String key="Fog">Fog / visibility</String>
|
||||
<String key="Season">Season</String>
|
||||
<String key="Temperature">Temperature</String>
|
||||
<String key="Visibility">Visibility (fog)</String>
|
||||
<String key="Overcast">Overcast</String>
|
||||
<String key="SeasonNow">Today</String>
|
||||
<String key="SeasonSpring">Spring</String>
|
||||
<String key="SeasonSummer">Summer</String>
|
||||
<String key="SeasonAutumn">Autumn</String>
|
||||
<String key="SeasonWinter">Winter</String>
|
||||
<String key="OvercastClear">Clear</String>
|
||||
<String key="OvercastFair">Fair</String>
|
||||
<String key="OvercastLight">Light clouds</String>
|
||||
<String key="OvercastModerate">Moderate clouds</String>
|
||||
<String key="OvercastHeavy">Heavy clouds</String>
|
||||
<String key="OvercastCloudy">Cloudy</String>
|
||||
<String key="OvercastStorm">Storm</String>
|
||||
<String key="WeatherHint">Weather changes are written to the scenery on launch.</String>
|
||||
<String key="Consist">Consist</String>
|
||||
<String key="Start">Start</String>
|
||||
<String key="ShowLatestLogTooltip">Open latest simulation log</String>
|
||||
@@ -57,7 +85,32 @@
|
||||
<String key="CatTruck">Truck</String>
|
||||
<String key="CatPeople">People</String>
|
||||
<String key="CatAnimals">Animals</String>
|
||||
<String key="CatWagons">Wagons</String>
|
||||
<String key="CatWagonsA">Wagons A</String>
|
||||
<String key="CatWagonsB">Wagons B</String>
|
||||
<String key="CatWagonsC">Wagons C</String>
|
||||
<String key="CatWagonsD">Wagons D</String>
|
||||
<String key="CatWagonsE">Wagons E</String>
|
||||
<String key="CatWagonsF">Wagons F</String>
|
||||
<String key="CatWagonsG">Wagons G</String>
|
||||
<String key="CatWagonsH">Wagons H</String>
|
||||
<String key="CatWagonsI">Wagons I</String>
|
||||
<String key="CatWagonsJ">Wagons J</String>
|
||||
<String key="CatWagonsK">Wagons K</String>
|
||||
<String key="CatWagonsL">Wagons L</String>
|
||||
<String key="CatWagonsM">Wagons M</String>
|
||||
<String key="CatWagonsN">Wagons N</String>
|
||||
<String key="CatWagonsO">Wagons O</String>
|
||||
<String key="CatWagonsP">Wagons P</String>
|
||||
<String key="CatWagonsQ">Wagons Q</String>
|
||||
<String key="CatWagonsR">Wagons R</String>
|
||||
<String key="CatWagonsS">Wagons S</String>
|
||||
<String key="CatWagonsT">Wagons T</String>
|
||||
<String key="CatWagonsU">Wagons U</String>
|
||||
<String key="CatWagonsV">Wagons V</String>
|
||||
<String key="CatWagonsW">Wagons W</String>
|
||||
<String key="CatWagonsX">Wagons X</String>
|
||||
<String key="CatWagonsY">Wagons Y</String>
|
||||
<String key="CatWagonsZ">Wagons Z</String>
|
||||
<String key="CatOther">Other</String>
|
||||
<String key="Brakes">Brakes</String>
|
||||
<String key="Loads">Loads</String>
|
||||
|
||||
@@ -8,12 +8,40 @@
|
||||
<String key="NavDepot">Zajezdnia</String>
|
||||
<String key="NavSettings">Ustawienia</String>
|
||||
<String key="NavControls">Sterowanie</String>
|
||||
<String key="Help">Pomoc</String>
|
||||
<String key="Version">Wersja:</String>
|
||||
|
||||
<!-- Widok ogolny -->
|
||||
<String key="Sceneries">Scenerie</String>
|
||||
<String key="Vehicles">Pojazdy</String>
|
||||
<String key="ScenarioDescription">Opis scenariusza</String>
|
||||
<String key="SceneryDescription">Opis scenerii</String>
|
||||
<String key="TabDescription">Opis</String>
|
||||
<String key="Weather">Pogoda</String>
|
||||
<String key="Timetable">Rozkład jazdy</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="Time">Godzina</String>
|
||||
<String key="Day">Dzień roku</String>
|
||||
<String key="Sky">Niebo</String>
|
||||
<String key="Fog">Mgła / widoczność</String>
|
||||
<String key="Season">Pora roku</String>
|
||||
<String key="Temperature">Temperatura</String>
|
||||
<String key="Visibility">Widoczność (mgła)</String>
|
||||
<String key="Overcast">Zachmurzenie</String>
|
||||
<String key="SeasonNow">Dziś</String>
|
||||
<String key="SeasonSpring">Wiosna</String>
|
||||
<String key="SeasonSummer">Lato</String>
|
||||
<String key="SeasonAutumn">Jesień</String>
|
||||
<String key="SeasonWinter">Zima</String>
|
||||
<String key="OvercastClear">Bezchmurnie</String>
|
||||
<String key="OvercastFair">Pogodnie</String>
|
||||
<String key="OvercastLight">Lekkie zachmurzenie</String>
|
||||
<String key="OvercastModerate">Umiarkowane zachmurzenie</String>
|
||||
<String key="OvercastHeavy">Duże zachmurzenie</String>
|
||||
<String key="OvercastCloudy">Pochmurno</String>
|
||||
<String key="OvercastStorm">Burza</String>
|
||||
<String key="WeatherHint">Zmiany pogody zostaną zapisane do scenerii przy uruchomieniu.</String>
|
||||
<String key="Consist">Zestawienie</String>
|
||||
<String key="Start">Uruchom</String>
|
||||
<String key="ShowLatestLogTooltip">Otwórz ostatni log symulatora</String>
|
||||
@@ -57,7 +85,32 @@
|
||||
<String key="CatTruck">Samochód ciężarowy</String>
|
||||
<String key="CatPeople">Ludzie</String>
|
||||
<String key="CatAnimals">Zwierzęta</String>
|
||||
<String key="CatWagons">Wagony</String>
|
||||
<String key="CatWagonsA">Wagony A</String>
|
||||
<String key="CatWagonsB">Wagony B</String>
|
||||
<String key="CatWagonsC">Wagony C</String>
|
||||
<String key="CatWagonsD">Wagony D</String>
|
||||
<String key="CatWagonsE">Wagony E</String>
|
||||
<String key="CatWagonsF">Wagony F</String>
|
||||
<String key="CatWagonsG">Wagony G</String>
|
||||
<String key="CatWagonsH">Wagony H</String>
|
||||
<String key="CatWagonsI">Wagony I</String>
|
||||
<String key="CatWagonsJ">Wagony J</String>
|
||||
<String key="CatWagonsK">Wagony K</String>
|
||||
<String key="CatWagonsL">Wagony L</String>
|
||||
<String key="CatWagonsM">Wagony M</String>
|
||||
<String key="CatWagonsN">Wagony N</String>
|
||||
<String key="CatWagonsO">Wagony O</String>
|
||||
<String key="CatWagonsP">Wagony P</String>
|
||||
<String key="CatWagonsQ">Wagony Q</String>
|
||||
<String key="CatWagonsR">Wagony R</String>
|
||||
<String key="CatWagonsS">Wagony S</String>
|
||||
<String key="CatWagonsT">Wagony T</String>
|
||||
<String key="CatWagonsU">Wagony U</String>
|
||||
<String key="CatWagonsV">Wagony V</String>
|
||||
<String key="CatWagonsW">Wagony W</String>
|
||||
<String key="CatWagonsX">Wagony X</String>
|
||||
<String key="CatWagonsY">Wagony Y</String>
|
||||
<String key="CatWagonsZ">Wagony Z</String>
|
||||
<String key="CatOther">Inne</String>
|
||||
<String key="Brakes">Hamulce</String>
|
||||
<String key="Loads">Ładunki</String>
|
||||
|
||||
Reference in New Issue
Block a user