Complete UI rebuild

This commit is contained in:
2026-06-23 18:20:09 +02:00
parent a7bb897fc6
commit b977d224e9
13 changed files with 1322 additions and 754 deletions

View File

@@ -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,29 +19,47 @@
</Grid.RowDefinitions>
<!-- Vehicle database browser (left, rows 0-1) -->
<sukiUi:GroupBox Grid.RowSpan="2"
Header="{Binding [VehicleDatabase], Source={x:Static starterNg:App.Loc}}">
<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"
Header="{Binding [LoadFromScenery], Source={x:Static starterNg:App.Loc}}">
<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"
Text="{Binding [Scenery], Source={x:Static starterNg:App.Loc}}" />
@@ -54,11 +71,11 @@
DropDownOpened="SceneryConsistCombo_OnDropDownOpened"
SelectionChanged="SceneryConsistCombo_OnSelectionChanged" />
</Grid>
</sukiUi:GroupBox>
</HeaderedContentControl>
<!-- Brakes / Loads editor (middle-right) -->
<sukiUi:GroupBox Grid.Column="1" Grid.Row="1"
Header="{Binding [SelectedVehicle], Source={x:Static starterNg:App.Loc}}">
<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">
<Button Name="newConsistButton" Classes="Flat" Cursor="Hand"
@@ -81,11 +98,11 @@
</TabItem>
</TabControl>
</DockPanel>
</sukiUi:GroupBox>
</HeaderedContentControl>
<!-- Consist (full-width bottom, horizontally scrollable) -->
<sukiUi:GroupBox Grid.Row="2" Grid.ColumnSpan="2"
Header="{Binding [Consist], Source={x:Static starterNg:App.Loc}}">
<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"
TextWrapping="Wrap" MaxWidth="520" Opacity="0.6" TextAlignment="Center"
@@ -95,6 +112,6 @@
VerticalAlignment="Center" Spacing="0" />
</ScrollViewer>
</Panel>
</sukiUi:GroupBox>
</HeaderedContentControl>
</Grid>
</UserControl>

View File

@@ -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;
var matched = _db.Textures
.Where(t => PassesFilters(t, search, hasSearch))
.OrderBy(t => t.TextureMini ?? t.Skinfile, StringComparer.OrdinalIgnoreCase)
.Take(MaxListRows)
.ToList();
// fold by class (the group's mini)
var grouped = matched
.GroupBy(ClassOf)
.OrderBy(g => g.Key, StringComparer.OrdinalIgnoreCase);
foreach (var group in grouped)
foreach (var texture in matched)
{
var list = group
.OrderBy(t => t.TextureMini ?? t.Skinfile, StringComparer.OrdinalIgnoreCase)
.ToList();
string header = string.IsNullOrEmpty(group.Key)
? App.Loc["Others"]
: group.Key;
var content = new StackPanel { Spacing = 2 };
var expander = new Expander
vehicleListBox.Items.Add(new ListBoxItem
{
Header = $"{header} ({list.Count})",
IsExpanded = autoExpand, // folded by default
HorizontalAlignment = HorizontalAlignment.Stretch
};
expander.Content = content;
bool populated = false;
void Populate()
{
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)
});
}
if (expander.IsExpanded)
Populate();
expander.Expanded += (_, _) => Populate();
browserStack.Children.Add(expander);
}
if (browserStack.Children.Count == 0)
{
browserStack.Children.Add(new TextBlock
{
Text = App.Loc["NoVehicles"],
Opacity = 0.6,
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(4)
Content = BrowserLabel(texture, _db.ResolveSet(texture)),
Tag = texture
});
}
if (vehicleListBox.Items.Count == 0)
vehicleListBox.Items.Add(new ListBoxItem
{
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 });

View File

@@ -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}}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<TextBlock TextWrapping="Wrap" Name="missionDescription">
</TextBlock>
<!-- 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" Margin="4" />
</ScrollViewer>
</TabItem>
<TabItem Header="{Binding [Weather], Source={x:Static starterNg:App.Loc}}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<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>
<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>
</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>
<!-- Right: scenery image (//$i) + scenery description (//$d) -->
<Grid Grid.Column="2" Grid.Row="0" RowSpacing="8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<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>
</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}}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<TextBlock TextWrapping="Wrap" Name="sceneryDescription">
</TextBlock>
</ScrollViewer>
</sukiUi:GroupBox>
<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">
</StackPanel>
</ScrollViewer>
</sukiUi:GroupBox>
<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}}">
</Button>
</HeaderedContentControl>
</Grid>
</UserControl>

View File

@@ -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
}
}
}

View File

@@ -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,380 +19,281 @@
</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">
<StackPanel Spacing="8" Orientation="Horizontal">
<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>
<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" />
<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}}" />
<CheckBox x:Name="PauseInactiveCb" Content="{Binding [PauseSimInactive], 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}}" />
<Slider x:Name="CursorSensitivitySlider" Minimum="1" Maximum="5" TickFrequency="1" MinWidth="200" MaxWidth="200"
IsSnapToTickEnabled="True" Value="3" />
</StackPanel>
<CheckBox x:Name="PauseStartCb" Content="{Binding [PauseSimStart], 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>
</ScrollViewer>
</TabItem>
<StackPanel Spacing="8" Orientation="Horizontal">
<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>
</StackPanel>
<!-- 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}}" />
<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}}" />
</ComboBox>
</StackPanel>
</StackPanel>
</ScrollViewer>
</TabItem>
<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}}"/>
<!-- 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}}" />
<ComboBox x:Name="SelectExeCb" MinWidth="350" MaxWidth="350" SelectedIndex="0">
<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}}" />
</StackPanel>
</ScrollViewer>
</TabItem>
<StackPanel Spacing="8" Orientation="Horizontal">
<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}}"/>
</ComboBox>
</StackPanel>
<!-- 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}}" />
<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}}" />
</ComboBox>
</StackPanel>
<StackPanel Spacing="8" Orientation="Horizontal">
<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>
<ComboBoxItem>1920x1080</ComboBoxItem>
<ComboBoxItem>1600x900</ComboBoxItem>
<ComboBoxItem>1366x768</ComboBoxItem>
<ComboBoxItem>1280x720</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Spacing="8" Orientation="Horizontal">
<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}%'}" />
</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 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 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" />
</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" />
</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"
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}}" />
<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" />
</StackPanel>
<StackPanel Spacing="8" Orientation="Horizontal">
<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}}" />
</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}}" />
<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'}" />
</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 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'}" />
</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'}" />
</StackPanel>
<StackPanel Spacing="8" Orientation="Horizontal">
<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}}" />
</ComboBox>
</StackPanel>
<StackPanel Spacing="8" Orientation="Horizontal">
<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}}" />
</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'}" />
</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}}" />
<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" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</TabItem>
</StackPanel>
</suki:SettingsLayoutItem.Content>
</suki:SettingsLayoutItem>
<!-- 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" />
</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}}" />
</StackPanel>
</ScrollViewer>
</TabItem>
<suki:SettingsLayoutItem Header="{Binding [Others], Source={x:Static starterNg:App.Loc}}">
<suki:SettingsLayoutItem.Content>
<StackPanel Spacing="8" Orientation="Vertical">
<StackPanel Spacing="8" Orientation="Horizontal">
<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>eu07.exe</ComboBoxItem>
</ComboBox>
</StackPanel>
<!-- 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" />
</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" />
</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" />
</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" />
</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" />
</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" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</TabItem>
<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>
<suki:SettingsLayoutItem Header="{Binding [Graphic], Source={x:Static starterNg:App.Loc}}">
<suki:SettingsLayoutItem.Content>
<StackPanel Spacing="8" Orientation="Vertical">
<StackPanel Spacing="8" Orientation="Horizontal">
<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}}"/>
</ComboBox>
</StackPanel>
<StackPanel Spacing="8" Orientation="Horizontal">
<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>
<ComboBoxItem>1920x1080</ComboBoxItem>
<ComboBoxItem>1600x900</ComboBoxItem>
<ComboBoxItem>1366x768</ComboBoxItem>
<ComboBoxItem>1280x720</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Spacing="8" Orientation="Horizontal">
<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}%'}" />
</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 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 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" />
</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" />
</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"
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}}"/>
<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" />
</StackPanel>
<StackPanel Spacing="8" Orientation="Horizontal">
<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}}"/>
</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}}"/>
<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'}" />
</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 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'}" />
</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'}" />
</StackPanel>
<StackPanel Spacing="8" Orientation="Horizontal">
<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}}"/>
</ComboBox>
</StackPanel>
<StackPanel Spacing="8" Orientation="Horizontal">
<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}}"/>
</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'}" />
</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}}"/>
<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" />
</StackPanel>
</StackPanel>
</suki:SettingsLayoutItem.Content>
</suki:SettingsLayoutItem>
<suki:SettingsLayoutItem Header="{Binding [Physics], Source={x:Static starterNg:App.Loc}}">
<suki:SettingsLayoutItem.Content>
<StackPanel Spacing="8" Orientation="Vertical">
<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" />
</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}}"/>
</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}}"/>
<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" />
</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" />
</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" />
</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" />
</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" />
</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" />
</StackPanel>
</StackPanel>
</suki:SettingsLayoutItem.Content>
</suki:SettingsLayoutItem>
<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}}"/>
</StackPanel>
</suki:SettingsLayoutItem.Content>
</suki:SettingsLayoutItem>
</objectModel:ObservableCollection>
</suki:SettingsLayout.Items>
</suki:SettingsLayout>
<!-- 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>
</ScrollViewer>
</TabItem>
</TabControl>
</DockPanel>
</UserControl>