mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-07-17 17:09:19 +02:00
Layout improvements, depot behavior improvements
This commit is contained in:
@@ -279,10 +279,24 @@ public class VehicleDatabase
|
|||||||
if (t.Group != null)
|
if (t.Group != null)
|
||||||
GroupsById.TryGetValue(t.Group, out g);
|
GroupsById.TryGetValue(t.Group, out g);
|
||||||
|
|
||||||
t.ResolvedCategory = g?.Category;
|
string mini = g != null && !string.IsNullOrEmpty(g.Mini)
|
||||||
t.ResolvedClass = g != null && !string.IsNullOrEmpty(g.Mini)
|
|
||||||
? g.Mini!
|
? g.Mini!
|
||||||
: t.MiniRef ?? "";
|
: t.MiniRef ?? "";
|
||||||
|
|
||||||
|
string? category = g?.Category;
|
||||||
|
|
||||||
|
// A "*" category is a wildcard: the actual category is the first letter
|
||||||
|
// of the mini (upper-cased, like the original Starter's UpCase(mini[1])
|
||||||
|
// wagon-typing), e.g. a "Bdhpumn" mini -> category "B".
|
||||||
|
if (category == "*")
|
||||||
|
{
|
||||||
|
string source = !string.IsNullOrEmpty(mini) ? mini : (t.TextureMini ?? "");
|
||||||
|
if (source.Length > 0)
|
||||||
|
category = char.ToUpperInvariant(source[0]).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
t.ResolvedCategory = category;
|
||||||
|
t.ResolvedClass = mini;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,10 +27,15 @@
|
|||||||
BorderThickness="0,0,0,1">
|
BorderThickness="0,0,0,1">
|
||||||
<Grid RowDefinitions="32,48">
|
<Grid RowDefinitions="32,48">
|
||||||
|
|
||||||
<!-- Row 0: title / caption strip (draggable; OS buttons top-right) -->
|
<!-- Row 0: title / caption strip. Transparent background makes the
|
||||||
<TextBlock Grid.Row="0" Text="Starter MaSzyna" VerticalAlignment="Center"
|
whole strip a drag handle (PointerPressed -> BeginMoveDrag),
|
||||||
Margin="14,0,0,0" FontSize="11"
|
and the OS buttons render at its top-right. -->
|
||||||
Foreground="{StaticResource CarbonFgDim}" />
|
<Border Grid.Row="0" Background="Transparent"
|
||||||
|
PointerPressed="TitleBar_OnPointerPressed">
|
||||||
|
<TextBlock Text="Starter MaSzyna" VerticalAlignment="Center"
|
||||||
|
Margin="14,0,0,0" FontSize="11"
|
||||||
|
Foreground="{StaticResource CarbonFgDim}" />
|
||||||
|
</Border>
|
||||||
|
|
||||||
<!-- Row 1: logo + navigation (left) and the right cluster (right) -->
|
<!-- Row 1: logo + navigation (left) and the right cluster (right) -->
|
||||||
<Grid Grid.Row="1" ColumnDefinitions="Auto,Auto,*,Auto">
|
<Grid Grid.Row="1" ColumnDefinitions="Auto,Auto,*,Auto">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using StarterNG.Classes;
|
using StarterNG.Classes;
|
||||||
@@ -32,6 +33,13 @@ public partial class MainWindow : Window
|
|||||||
LangCombo.SelectedIndex = App.Loc.CurrentLanguage == "Polski" ? 0 : 1;
|
LangCombo.SelectedIndex = App.Loc.CurrentLanguage == "Polski" ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lets the user drag the window by the top caption strip.
|
||||||
|
private void TitleBar_OnPointerPressed(object? sender, PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||||
|
BeginMoveDrag(e);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Navigation: show the page whose RadioButton was just checked ──────────
|
// ── Navigation: show the page whose RadioButton was just checked ──────────
|
||||||
private void Nav_OnCheckedChanged(object? sender, RoutedEventArgs e)
|
private void Nav_OnCheckedChanged(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||||
<AssemblyVersion>1.0.0.1 </AssemblyVersion>
|
<AssemblyVersion>1.0.0.1</AssemblyVersion>
|
||||||
<FileVersion>1.0.0.1</FileVersion>
|
<FileVersion>1.0.0.1</FileVersion>
|
||||||
<AssemblyName>Starter</AssemblyName>
|
<AssemblyName>Starter</AssemblyName>
|
||||||
<Company>eu07.pl</Company>
|
<Company>eu07.pl</Company>
|
||||||
|
|||||||
@@ -171,11 +171,13 @@
|
|||||||
<Setter Property="Background" Value="{StaticResource CarbonAccent}" />
|
<Setter Property="Background" Value="{StaticResource CarbonAccent}" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<!-- ── Tabs: smaller headers (Fluent's default is oversized) ──────────── -->
|
<!-- ── Tabs: smaller headers than Fluent's default, but tall enough that the
|
||||||
|
green selection pipe at the bottom doesn't touch the label. ─────────── -->
|
||||||
<Style Selector="TabItem">
|
<Style Selector="TabItem">
|
||||||
<Setter Property="FontSize" Value="14" />
|
<Setter Property="FontSize" Value="14" />
|
||||||
<Setter Property="MinHeight" Value="0" />
|
<Setter Property="MinHeight" Value="38" />
|
||||||
<Setter Property="Padding" Value="10,6" />
|
<Setter Property="Padding" Value="12,4,12,8" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<!-- ── Compact list (tight rows that fill the available space) ─────────── -->
|
<!-- ── Compact list (tight rows that fill the available space) ─────────── -->
|
||||||
@@ -187,4 +189,14 @@
|
|||||||
<Setter Property="MinHeight" Value="0" />
|
<Setter Property="MinHeight" Value="0" />
|
||||||
<Setter Property="FontSize" Value="12" />
|
<Setter Property="FontSize" Value="12" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<!-- ── Compact tree (tight scenery rows) ──────────────────────────────── -->
|
||||||
|
<Style Selector="TreeView.Compact TreeViewItem">
|
||||||
|
<Setter Property="MinHeight" Value="22" />
|
||||||
|
<Setter Property="FontSize" Value="12" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TreeView.Compact TreeViewItem /template/ Border#PART_LayoutRoot">
|
||||||
|
<Setter Property="MinHeight" Value="22" />
|
||||||
|
<Setter Property="Padding" Value="4,1" />
|
||||||
|
</Style>
|
||||||
</Styles>
|
</Styles>
|
||||||
|
|||||||
@@ -13,9 +13,11 @@
|
|||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<!-- Consists list and the brakes/loads editor share the upper area
|
||||||
<RowDefinition />
|
evenly, so the editor has room for all its controls. -->
|
||||||
<RowDefinition Height="220" />
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="*" MinHeight="210" />
|
||||||
|
<RowDefinition Height="190" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- Vehicle database browser (left, rows 0-1) -->
|
<!-- Vehicle database browser (left, rows 0-1) -->
|
||||||
@@ -57,43 +59,31 @@
|
|||||||
</DockPanel>
|
</DockPanel>
|
||||||
</HeaderedContentControl>
|
</HeaderedContentControl>
|
||||||
|
|
||||||
<!-- Scenery + load consist (top-right) -->
|
<!-- Consists present on the scenery selected in the Scenarios tab.
|
||||||
|
Selecting one loads it into the editor below. -->
|
||||||
<HeaderedContentControl Grid.Column="1" Grid.Row="0" Theme="{StaticResource GroupBox}"
|
<HeaderedContentControl Grid.Column="1" Grid.Row="0" Theme="{StaticResource GroupBox}"
|
||||||
Header="{Binding [LoadFromScenery], Source={x:Static starterNg:App.Loc}}">
|
Header="{Binding [MapConsists], Source={x:Static starterNg:App.Loc}}">
|
||||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto" ColumnSpacing="8" RowSpacing="6">
|
<DockPanel LastChildFill="True">
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"
|
<TextBlock Name="mapSceneryLabel" DockPanel.Dock="Top" FontSize="12"
|
||||||
Text="{Binding [Scenery], Source={x:Static starterNg:App.Loc}}" />
|
Opacity="0.8" Margin="0,0,0,6" TextTrimming="CharacterEllipsis" />
|
||||||
<ComboBox Name="sceneryCombo" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch"
|
<ListBox Name="sceneryConsistList" Classes="Compact"
|
||||||
SelectionChanged="SceneryCombo_OnSelectionChanged" />
|
SelectionChanged="SceneryConsistList_OnSelectionChanged" />
|
||||||
<ComboBox Name="sceneryConsistCombo" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
|
</DockPanel>
|
||||||
HorizontalAlignment="Stretch" MaxDropDownHeight="420"
|
|
||||||
PlaceholderText="{Binding [LoadFromScenery], Source={x:Static starterNg:App.Loc}}"
|
|
||||||
DropDownOpened="SceneryConsistCombo_OnDropDownOpened"
|
|
||||||
SelectionChanged="SceneryConsistCombo_OnSelectionChanged" />
|
|
||||||
</Grid>
|
|
||||||
</HeaderedContentControl>
|
</HeaderedContentControl>
|
||||||
|
|
||||||
<!-- Brakes / Loads editor (middle-right) -->
|
<!-- Brakes / Loads editor (middle-right) -->
|
||||||
<HeaderedContentControl Grid.Column="1" Grid.Row="1" Theme="{StaticResource GroupBox}"
|
<HeaderedContentControl Grid.Column="1" Grid.Row="1" Theme="{StaticResource GroupBox}"
|
||||||
Header="{Binding [SelectedVehicle], Source={x:Static starterNg:App.Loc}}">
|
Header="{Binding [SelectedVehicle], Source={x:Static starterNg:App.Loc}}">
|
||||||
<DockPanel LastChildFill="True">
|
<DockPanel LastChildFill="True">
|
||||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Spacing="8" Margin="0,0,0,8">
|
|
||||||
<Button Name="newConsistButton" Classes="Flat" Cursor="Hand"
|
|
||||||
Click="NewConsist_OnClick"
|
|
||||||
Content="{Binding [NewConsist], Source={x:Static starterNg:App.Loc}}" />
|
|
||||||
<TextBlock Name="summaryLabel" VerticalAlignment="Center" Opacity="0.8" FontSize="12" />
|
|
||||||
</StackPanel>
|
|
||||||
<TabControl>
|
<TabControl>
|
||||||
<TabItem Header="{Binding [Brakes], Source={x:Static starterNg:App.Loc}}">
|
<TabItem Header="{Binding [Brakes], Source={x:Static starterNg:App.Loc}}">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
<TextBlock Name="brakesContent" TextWrapping="Wrap" FontSize="12"
|
<StackPanel Name="brakesPanel" Margin="4" Spacing="8" />
|
||||||
LineHeight="18" Margin="4" />
|
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="{Binding [Loads], Source={x:Static starterNg:App.Loc}}">
|
<TabItem Header="{Binding [Loads], Source={x:Static starterNg:App.Loc}}">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
<TextBlock Name="loadsContent" TextWrapping="Wrap" FontSize="12"
|
<StackPanel Name="loadsPanel" Margin="4" Spacing="8" />
|
||||||
LineHeight="18" Margin="4" />
|
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
@@ -40,8 +41,8 @@ public partial class Depot : UserControl
|
|||||||
// debounces search input so we don't rebuild on every keystroke
|
// debounces search input so we don't rebuild on every keystroke
|
||||||
private DispatcherTimer? _searchTimer;
|
private DispatcherTimer? _searchTimer;
|
||||||
|
|
||||||
// scenery-consist previews are built lazily on first dropdown open
|
// the scenery whose consists are listed on the right (from the Scenarios tab)
|
||||||
private bool _consistPreviewsBuilt;
|
private Scenery? _listScenery;
|
||||||
|
|
||||||
// active database filters
|
// active database filters
|
||||||
private Func<string?, bool>? _categoryFilter; // matches a category letter, null = none chosen
|
private Func<string?, bool>? _categoryFilter; // matches a category letter, null = none chosen
|
||||||
@@ -141,16 +142,26 @@ public partial class Depot : UserControl
|
|||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
PopulateCategoryCombo(); // -> populates class combo -> builds browser
|
PopulateCategoryCombo(); // -> populates class combo -> builds browser
|
||||||
PopulateSceneryCombo(); // -> populates consist combo for the first scenery
|
PopulateSceneryConsists(); // -> lists the current scenery's consists
|
||||||
RebuildConsist();
|
RebuildConsist();
|
||||||
}, DispatcherPriority.Background);
|
}, DispatcherPriority.Background);
|
||||||
|
|
||||||
// When the tab is shown, pick up the consist selected in the scenery view.
|
// Switching tabs only toggles IsVisible (the view stays in the visual
|
||||||
|
// tree), so refresh whenever the depot becomes visible - this picks up a
|
||||||
|
// scenery changed in the Scenarios tab in the meantime.
|
||||||
AttachedToVisualTree += (_, _) => SyncFromSelection();
|
AttachedToVisualTree += (_, _) => SyncFromSelection();
|
||||||
|
PropertyChanged += (_, e) =>
|
||||||
|
{
|
||||||
|
if (e.Property == IsVisibleProperty && IsVisible)
|
||||||
|
SyncFromSelection();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SyncFromSelection()
|
private void SyncFromSelection()
|
||||||
{
|
{
|
||||||
|
// refresh the on-map consists list to the scenery picked in the Scenarios tab
|
||||||
|
PopulateSceneryConsists();
|
||||||
|
|
||||||
var trainset = AppState.Instance.CurrentTrainset;
|
var trainset = AppState.Instance.CurrentTrainset;
|
||||||
if (trainset != null && !ReferenceEquals(trainset, _editingTrainset))
|
if (trainset != null && !ReferenceEquals(trainset, _editingTrainset))
|
||||||
LoadTrainset(trainset);
|
LoadTrainset(trainset);
|
||||||
@@ -497,116 +508,62 @@ public partial class Depot : UserControl
|
|||||||
|
|
||||||
// ------------------------------------------------------------- scenery combos
|
// ------------------------------------------------------------- scenery combos
|
||||||
|
|
||||||
private void PopulateSceneryCombo()
|
// Lists the consists of the scenery currently selected in the Scenarios tab,
|
||||||
|
// so they can be picked and edited here.
|
||||||
|
private void PopulateSceneryConsists()
|
||||||
{
|
{
|
||||||
_suppress = true;
|
_suppress = true;
|
||||||
sceneryCombo.Items.Clear();
|
sceneryConsistList.Items.Clear();
|
||||||
foreach (var scenery in _sceneries)
|
|
||||||
|
_listScenery = AppState.Instance.CurrentScenery ?? _sceneries.FirstOrDefault();
|
||||||
|
mapSceneryLabel.Text = _listScenery != null
|
||||||
|
? Path.GetFileNameWithoutExtension(_listScenery.Path)
|
||||||
|
: App.Loc["NoSceneryConsists"];
|
||||||
|
|
||||||
|
if (_listScenery != null)
|
||||||
{
|
{
|
||||||
sceneryCombo.Items.Add(new ComboBoxItem
|
foreach (var trainset in _listScenery.Trainsets)
|
||||||
{
|
|
||||||
Content = Path.GetFileNameWithoutExtension(scenery.Path),
|
|
||||||
Tag = scenery
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (sceneryCombo.Items.Count > 0)
|
|
||||||
sceneryCombo.SelectedIndex = 0;
|
|
||||||
_suppress = false;
|
|
||||||
|
|
||||||
PopulateConsistCombo(_sceneries.FirstOrDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PopulateConsistCombo(Scenery? scenery)
|
|
||||||
{
|
|
||||||
_suppress = true;
|
|
||||||
sceneryConsistCombo.Items.Clear();
|
|
||||||
|
|
||||||
if (scenery != null)
|
|
||||||
{
|
|
||||||
foreach (var trainset in scenery.Trainsets)
|
|
||||||
{
|
{
|
||||||
if (trainset.Vehicles.Count == 0)
|
if (trainset.Vehicles.Count == 0)
|
||||||
continue;
|
continue;
|
||||||
if (trainset.Vehicles.All(v => string.IsNullOrWhiteSpace(v.Name)))
|
if (trainset.Vehicles.All(v => string.IsNullOrWhiteSpace(v.Name)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// lightweight text content now; the rich mini-preview is built
|
var entry = new ListBoxItem
|
||||||
// lazily the first time the dropdown is opened
|
|
||||||
sceneryConsistCombo.Items.Add(new ComboBoxItem
|
|
||||||
{
|
{
|
||||||
Content = ConsistSummary(trainset),
|
Content = ConsistText(trainset),
|
||||||
Tag = trainset
|
Tag = trainset
|
||||||
});
|
};
|
||||||
|
sceneryConsistList.Items.Add(entry);
|
||||||
|
|
||||||
|
if (ReferenceEquals(trainset, AppState.Instance.CurrentTrainset))
|
||||||
|
sceneryConsistList.SelectedItem = entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_consistPreviewsBuilt = false;
|
|
||||||
sceneryConsistCombo.SelectedItem = null;
|
|
||||||
_suppress = false;
|
_suppress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ConsistSummary(Trainset trainset)
|
// Selecting a consist on the map loads it into the editor below.
|
||||||
{
|
private void SceneryConsistList_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||||
string text = string.Join(" + ", trainset.Vehicles.Select(v => v.SkinFile));
|
|
||||||
return text.Length > 70 ? text[..67] + "..." : text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Builds the mini-thumbnail previews only once, on first dropdown open.
|
|
||||||
private void SceneryConsistCombo_OnDropDownOpened(object? sender, EventArgs e)
|
|
||||||
{
|
|
||||||
if (_consistPreviewsBuilt) return;
|
|
||||||
_consistPreviewsBuilt = true;
|
|
||||||
|
|
||||||
foreach (var obj in sceneryConsistCombo.Items)
|
|
||||||
if (obj is ComboBoxItem { Tag: Trainset trainset } item)
|
|
||||||
item.Content = BuildConsistPreview(trainset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combo item: row of mini thumbnails on top, skin names underneath.
|
|
||||||
private Control BuildConsistPreview(Trainset trainset)
|
|
||||||
{
|
|
||||||
var minis = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 2 };
|
|
||||||
var names = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 6 };
|
|
||||||
|
|
||||||
foreach (var v in trainset.Vehicles)
|
|
||||||
{
|
|
||||||
var bmp = GetMiniBitmap(_db.MiniForSkin(v.SkinFile) ?? v.MiniName, 20);
|
|
||||||
if (bmp != null)
|
|
||||||
minis.Children.Add(new Image { Source = bmp, Height = 20, Stretch = Stretch.Uniform });
|
|
||||||
else
|
|
||||||
minis.Children.Add(new Border
|
|
||||||
{
|
|
||||||
Height = 20, Width = 32, Background = Placeholder, CornerRadius = new CornerRadius(3)
|
|
||||||
});
|
|
||||||
|
|
||||||
names.Children.Add(new TextBlock { Text = v.SkinFile, FontSize = 10, Opacity = 0.8 });
|
|
||||||
}
|
|
||||||
|
|
||||||
var panel = new StackPanel { Spacing = 3 };
|
|
||||||
panel.Children.Add(minis);
|
|
||||||
panel.Children.Add(names);
|
|
||||||
return panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SceneryCombo_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
{
|
||||||
if (_suppress) return;
|
if (_suppress) return;
|
||||||
var scenery = (sceneryCombo.SelectedItem as ComboBoxItem)?.Tag as Scenery;
|
if (sceneryConsistList.SelectedItem is not ListBoxItem { Tag: Trainset trainset })
|
||||||
PopulateConsistCombo(scenery);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Selecting a scenery consist binds the editor to that trainset.
|
|
||||||
private void SceneryConsistCombo_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
if (_suppress) return;
|
|
||||||
if (sceneryConsistCombo.SelectedItem is not ComboBoxItem { Tag: Trainset trainset })
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AppState.Instance.CurrentScenery = (sceneryCombo.SelectedItem as ComboBoxItem)?.Tag as Scenery;
|
AppState.Instance.CurrentScenery = _listScenery;
|
||||||
AppState.Instance.CurrentTrainset = trainset;
|
AppState.Instance.CurrentTrainset = trainset;
|
||||||
LoadTrainset(trainset);
|
LoadTrainset(trainset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Plain-text label for a scenery consist (no thumbnails).
|
||||||
|
private static string ConsistText(Trainset trainset)
|
||||||
|
{
|
||||||
|
string text = string.Join(" + ", trainset.Vehicles.Select(v => v.SkinFile));
|
||||||
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
|
text = string.Join(" + ", trainset.Vehicles.Select(v => v.Name));
|
||||||
|
return text.Length > 80 ? text[..77] + "…" : text;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------ consist edits
|
// ------------------------------------------------------------ consist edits
|
||||||
|
|
||||||
private Dynamic MakeDynamic(VehicleTexture texture)
|
private Dynamic MakeDynamic(VehicleTexture texture)
|
||||||
@@ -728,7 +685,6 @@ public partial class Depot : UserControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
emptyHint.IsVisible = _consist.Count == 0;
|
emptyHint.IsVisible = _consist.Count == 0;
|
||||||
UpdateSummary();
|
|
||||||
UpdateDetails();
|
UpdateDetails();
|
||||||
WriteBackToScenery();
|
WriteBackToScenery();
|
||||||
}
|
}
|
||||||
@@ -849,22 +805,123 @@ public partial class Depot : UserControl
|
|||||||
Cursor = _hand,
|
Cursor = _hand,
|
||||||
Child = inner
|
Child = inner
|
||||||
};
|
};
|
||||||
card.PointerPressed += (_, _) =>
|
card.PointerPressed += (_, e) =>
|
||||||
{
|
{
|
||||||
|
// left button only - right-click is reserved for the context popup,
|
||||||
|
// and rebuilding here would detach the card it anchors to
|
||||||
|
if (!e.GetCurrentPoint(card).Properties.IsLeftButtonPressed)
|
||||||
|
return;
|
||||||
|
|
||||||
_selected = item;
|
_selected = item;
|
||||||
RebuildConsist();
|
RebuildConsist();
|
||||||
|
// mirror the click into the database browser: pick the category and
|
||||||
|
// highlight this vehicle's entry there
|
||||||
|
SelectInBrowser(item.Cars[0]);
|
||||||
|
};
|
||||||
|
// right-click a vehicle to set its wagon number (node "#<n>" suffix)
|
||||||
|
card.ContextRequested += (_, args) =>
|
||||||
|
{
|
||||||
|
ShowWagonNumberFlyout(card, item.Cars[0]);
|
||||||
|
args.Handled = true;
|
||||||
};
|
};
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Selects, in the left database browser, the category and the list entry of the
|
||||||
|
// given vehicle - so clicking a consist car reveals it in the database.
|
||||||
|
private void SelectInBrowser(Dynamic car)
|
||||||
|
{
|
||||||
|
var texture = _db.TextureForSkin(car.SkinFile);
|
||||||
|
if (texture is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string? category = CategoryOf(texture);
|
||||||
|
var catItem = categoryCombo.Items.OfType<ComboBoxItem>()
|
||||||
|
.FirstOrDefault(it => it.Tag is Func<string?, bool> f && f(category));
|
||||||
|
|
||||||
|
if (catItem != null && !ReferenceEquals(categoryCombo.SelectedItem, catItem))
|
||||||
|
{
|
||||||
|
categoryCombo.SelectedItem = catItem; // -> rebuilds class combo (class=all) + list
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// same category already chosen: drop any class filter so the entry shows
|
||||||
|
_suppress = true;
|
||||||
|
classCombo.SelectedIndex = -1;
|
||||||
|
_suppress = false;
|
||||||
|
_classFilter = null;
|
||||||
|
BuildList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// highlight the matching entry (selection drives the mini + Add button)
|
||||||
|
foreach (var obj in vehicleListBox.Items)
|
||||||
|
if (obj is ListBoxItem { Tag: VehicleTexture t } entry &&
|
||||||
|
string.Equals(t.Skinfile, texture.Skinfile, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
string.Equals(t.Directory, texture.Directory, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
vehicleListBox.SelectedItem = entry;
|
||||||
|
entry.BringIntoView();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "#<n>" number written into the node name (e.g. EU07-005#11). 0 = none.
|
||||||
|
private static int GetWagonNumber(Dynamic car)
|
||||||
|
{
|
||||||
|
string name = car.Name ?? "";
|
||||||
|
int hash = name.LastIndexOf('#');
|
||||||
|
if (hash >= 0 && hash + 1 < name.Length && name[(hash + 1)..].All(char.IsDigit)
|
||||||
|
&& int.TryParse(name[(hash + 1)..], out int n))
|
||||||
|
return n;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetWagonNumber(Dynamic car, int number)
|
||||||
|
{
|
||||||
|
string name = car.Name ?? "";
|
||||||
|
int hash = name.LastIndexOf('#');
|
||||||
|
string baseName = (hash >= 0 && hash + 1 < name.Length && name[(hash + 1)..].All(char.IsDigit))
|
||||||
|
? name[..hash]
|
||||||
|
: name;
|
||||||
|
car.Name = number > 0 ? $"{baseName}#{number}" : baseName;
|
||||||
|
RebuildConsist();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Small right-click popup to type a wagon number for the vehicle.
|
||||||
|
private void ShowWagonNumberFlyout(Control anchor, Dynamic car)
|
||||||
|
{
|
||||||
|
var num = new NumericUpDown
|
||||||
|
{
|
||||||
|
Minimum = 0, Maximum = 99999, Increment = 1, FormatString = "0",
|
||||||
|
Value = GetWagonNumber(car), MinWidth = 120
|
||||||
|
};
|
||||||
|
var apply = new Button { Content = App.Loc["Set"], HorizontalAlignment = HorizontalAlignment.Right };
|
||||||
|
apply.Classes.Add("Flat");
|
||||||
|
apply.Classes.Add("Accent");
|
||||||
|
|
||||||
|
var panel = new StackPanel { Spacing = 6, Margin = new Thickness(8), MinWidth = 160 };
|
||||||
|
panel.Children.Add(new TextBlock { Text = App.Loc["WagonNumber"], FontWeight = FontWeight.Bold, FontSize = 12 });
|
||||||
|
panel.Children.Add(num);
|
||||||
|
panel.Children.Add(apply);
|
||||||
|
|
||||||
|
var flyout = new Flyout { Content = panel, Placement = PlacementMode.Top };
|
||||||
|
apply.Click += (_, _) =>
|
||||||
|
{
|
||||||
|
SetWagonNumber(car, (int)(num.Value ?? 0));
|
||||||
|
flyout.Hide();
|
||||||
|
};
|
||||||
|
flyout.ShowAt(anchor);
|
||||||
|
}
|
||||||
|
|
||||||
private static string UnitLabel(ConsistItem item)
|
private static string UnitLabel(ConsistItem item)
|
||||||
{
|
{
|
||||||
string key = UnitKey(item.Cars[0]);
|
string key = UnitKey(item.Cars[0]);
|
||||||
return item.Cars.Count > 1 ? $"{key} [{item.Cars.Count}]" : key;
|
return item.Cars.Count > 1 ? $"{key} [{item.Cars.Count}]" : key;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Coupling indicator between two units. Hovering shows the 8-bit coupling
|
// Coupling indicator between two units. Click it to open a (clickable) flyout
|
||||||
// editor for the left unit's last car (placeholder; full editing comes later).
|
// with the 8-bit coupling editor for the left unit's last car - a flyout, not a
|
||||||
|
// tooltip, so it stays open while you toggle the options.
|
||||||
private Control BuildCoupler(ConsistItem item)
|
private Control BuildCoupler(ConsistItem item)
|
||||||
{
|
{
|
||||||
var glyph = new TextBlock
|
var glyph = new TextBlock
|
||||||
@@ -875,17 +932,21 @@ public partial class Depot : UserControl
|
|||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
HorizontalAlignment = HorizontalAlignment.Center
|
HorizontalAlignment = HorizontalAlignment.Center
|
||||||
};
|
};
|
||||||
var coupler = new Border
|
var coupler = new Button
|
||||||
{
|
{
|
||||||
Width = 18,
|
|
||||||
Padding = new Thickness(2),
|
Padding = new Thickness(2),
|
||||||
|
MinWidth = 0,
|
||||||
Cursor = _hand,
|
Cursor = _hand,
|
||||||
Background = Brushes.Transparent,
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
Child = glyph
|
Content = glyph,
|
||||||
|
Flyout = new Flyout
|
||||||
|
{
|
||||||
|
Content = BuildCouplingBox(item.Cars[^1]),
|
||||||
|
Placement = PlacementMode.Top
|
||||||
|
}
|
||||||
};
|
};
|
||||||
ToolTip.SetTip(coupler, BuildCouplingBox(item.Cars[^1]));
|
coupler.Classes.Add("Basic");
|
||||||
ToolTip.SetShowDelay(coupler, 150);
|
ToolTip.SetTip(coupler, App.Loc["Coupling"]);
|
||||||
return coupler;
|
return coupler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -914,40 +975,7 @@ public partial class Depot : UserControl
|
|||||||
panel.Children.Add(check);
|
panel.Children.Add(check);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Brake-rack setting (the "B" parameter of the coupling field).
|
// The brake setting lives in the Brakes tab now, not here.
|
||||||
panel.Children.Add(new TextBlock
|
|
||||||
{
|
|
||||||
Text = "Brake mode",
|
|
||||||
FontWeight = FontWeight.Bold,
|
|
||||||
FontSize = 11,
|
|
||||||
Margin = new Thickness(0, 6, 0, 2)
|
|
||||||
});
|
|
||||||
|
|
||||||
var brakeCombo = new ComboBox { FontSize = 11, MinWidth = 90 };
|
|
||||||
brakeCombo.Items.Add(new ComboBoxItem { Content = "(none)", Tag = null });
|
|
||||||
foreach (string mode in BrakeSetting.Modes)
|
|
||||||
brakeCombo.Items.Add(new ComboBoxItem { Content = mode, Tag = mode });
|
|
||||||
|
|
||||||
string? current = d.Coupling.GetBrake()?.Mode;
|
|
||||||
brakeCombo.SelectedIndex = current is null
|
|
||||||
? 0
|
|
||||||
: Array.IndexOf(BrakeSetting.Modes, current) + 1;
|
|
||||||
|
|
||||||
brakeCombo.SelectionChanged += (_, _) =>
|
|
||||||
{
|
|
||||||
if ((brakeCombo.SelectedItem as ComboBoxItem)?.Tag is not string mode)
|
|
||||||
{
|
|
||||||
d.Coupling.SetBrake(null);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var brake = d.Coupling.GetBrake() ?? new BrakeSetting();
|
|
||||||
brake.Mode = mode;
|
|
||||||
d.Coupling.SetBrake(brake);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
panel.Children.Add(brakeCombo);
|
|
||||||
|
|
||||||
return new Border { Padding = new Thickness(8), Child = panel };
|
return new Border { Padding = new Thickness(8), Child = panel };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -983,32 +1011,177 @@ public partial class Depot : UserControl
|
|||||||
return btn;
|
return btn;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateSummary()
|
// Brakes / Loads editors for the selected unit (applied to all of its cars).
|
||||||
{
|
|
||||||
int cars = _consist.Sum(i => i.Cars.Count);
|
|
||||||
int crewed = _consist.Count(i => i.Driver is eDriverType.Headdriver or eDriverType.Reardriver);
|
|
||||||
summaryLabel.Text = $"{cars} {App.Loc["Vehicles"]} · {crewed} {App.Loc["Crew"]}";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Brakes / Loads tabs are placeholders bound to the selected unit.
|
|
||||||
private void UpdateDetails()
|
private void UpdateDetails()
|
||||||
{
|
{
|
||||||
|
brakesPanel.Children.Clear();
|
||||||
|
loadsPanel.Children.Clear();
|
||||||
|
|
||||||
if (_selected is null)
|
if (_selected is null)
|
||||||
{
|
{
|
||||||
brakesContent.Text = App.Loc["SelectVehicleHint"];
|
brakesPanel.Children.Add(DetailHint(App.Loc["SelectVehicleHint"]));
|
||||||
loadsContent.Text = App.Loc["SelectVehicleHint"];
|
loadsPanel.Children.Add(DetailHint(App.Loc["SelectVehicleHint"]));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lead = _selected.Cars[0];
|
BuildBrakesEditor(_selected);
|
||||||
string title = _selected.Grouped ? UnitLabel(_selected) : lead.Name;
|
BuildLoadsEditor(_selected);
|
||||||
brakesContent.Text =
|
}
|
||||||
$"{App.Loc["Vehicle"]}: {title}\n" +
|
|
||||||
$"cars: {string.Join(", ", _selected.Cars.Select(c => c.SkinFile))}\n\n" +
|
private static TextBlock DetailHint(string text) => new()
|
||||||
$"{App.Loc["ComingSoon"]}";
|
{
|
||||||
loadsContent.Text =
|
Text = text, Opacity = 0.6, FontSize = 12, TextWrapping = TextWrapping.Wrap
|
||||||
$"{App.Loc["Vehicle"]}: {title}\n\n" +
|
};
|
||||||
$"{App.Loc["ComingSoon"]}";
|
|
||||||
|
private static TextBlock UnitTitle(ConsistItem item) => new()
|
||||||
|
{
|
||||||
|
Text = item.Grouped ? UnitLabel(item) : item.Cars[0].Name,
|
||||||
|
FontWeight = FontWeight.Bold, FontSize = 12
|
||||||
|
};
|
||||||
|
|
||||||
|
// Row of "<label> <control>".
|
||||||
|
private static Control LabeledRow(string label, Control control)
|
||||||
|
{
|
||||||
|
var sp = new StackPanel { Orientation = Orientation.Horizontal, Spacing = 8 };
|
||||||
|
sp.Children.Add(new TextBlock
|
||||||
|
{
|
||||||
|
Text = label, VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
FontSize = 12, MinWidth = 120
|
||||||
|
});
|
||||||
|
sp.Children.Add(control);
|
||||||
|
return sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddOption(ComboBox combo, string content, string? tag)
|
||||||
|
=> combo.Items.Add(new ComboBoxItem { Content = content, Tag = tag });
|
||||||
|
|
||||||
|
// Brake mode / load adaptation / switch, written into each car's coupling "B" param.
|
||||||
|
private void BuildBrakesEditor(ConsistItem item)
|
||||||
|
{
|
||||||
|
var brake = item.Cars[0].Coupling.GetBrake();
|
||||||
|
|
||||||
|
var modeCombo = new ComboBox { FontSize = 12, MinWidth = 150 };
|
||||||
|
AddOption(modeCombo, App.Loc["None"], null);
|
||||||
|
foreach (var m in BrakeSetting.Modes) AddOption(modeCombo, m, m);
|
||||||
|
modeCombo.SelectedIndex = brake?.Mode is { } mode
|
||||||
|
? Array.IndexOf(BrakeSetting.Modes, mode) + 1 : 0;
|
||||||
|
|
||||||
|
string?[] loads = { null, "T", "H", "F", "A" };
|
||||||
|
var loadCombo = new ComboBox { FontSize = 12, MinWidth = 150 };
|
||||||
|
foreach (var l in loads) AddOption(loadCombo, l ?? App.Loc["None"], l);
|
||||||
|
loadCombo.SelectedIndex = Math.Max(0, Array.IndexOf(loads, brake?.Load));
|
||||||
|
|
||||||
|
string?[] switches = { null, "0", "1", "A" };
|
||||||
|
var switchCombo = new ComboBox { FontSize = 12, MinWidth = 150 };
|
||||||
|
foreach (var s in switches) AddOption(switchCombo, s ?? App.Loc["None"], s);
|
||||||
|
switchCombo.SelectedIndex = Math.Max(0, Array.IndexOf(switches, brake?.Switch));
|
||||||
|
|
||||||
|
void Apply()
|
||||||
|
{
|
||||||
|
string? selMode = (modeCombo.SelectedItem as ComboBoxItem)?.Tag as string;
|
||||||
|
if (selMode is null)
|
||||||
|
{
|
||||||
|
foreach (var c in item.Cars) c.Coupling.SetBrake(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var b = new BrakeSetting
|
||||||
|
{
|
||||||
|
Mode = selMode,
|
||||||
|
Load = (loadCombo.SelectedItem as ComboBoxItem)?.Tag as string,
|
||||||
|
Switch = (switchCombo.SelectedItem as ComboBoxItem)?.Tag as string
|
||||||
|
};
|
||||||
|
foreach (var c in item.Cars) c.Coupling.SetBrake(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
modeCombo.SelectionChanged += (_, _) => Apply();
|
||||||
|
loadCombo.SelectionChanged += (_, _) => Apply();
|
||||||
|
switchCombo.SelectionChanged += (_, _) => Apply();
|
||||||
|
|
||||||
|
brakesPanel.Children.Add(UnitTitle(item));
|
||||||
|
brakesPanel.Children.Add(LabeledRow(App.Loc["BrakeMode"], modeCombo));
|
||||||
|
brakesPanel.Children.Add(LabeledRow(App.Loc["BrakeLoad"], loadCombo));
|
||||||
|
brakesPanel.Children.Add(LabeledRow(App.Loc["BrakeSwitch"], switchCombo));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cargo type + amount, written into each car's node::dynamic trailing params.
|
||||||
|
private void BuildLoadsEditor(ConsistItem item)
|
||||||
|
{
|
||||||
|
var lead = item.Cars[0];
|
||||||
|
|
||||||
|
// Suggest cargo names from data/load_weights.txt (same source the original
|
||||||
|
// Starter uses), while still allowing a free-typed value.
|
||||||
|
var typeBox = new AutoCompleteBox
|
||||||
|
{
|
||||||
|
FontSize = 12,
|
||||||
|
MinWidth = 200,
|
||||||
|
Text = lead.LoadType ?? "",
|
||||||
|
ItemsSource = LoadTypes(),
|
||||||
|
FilterMode = AutoCompleteFilterMode.ContainsOrdinal
|
||||||
|
};
|
||||||
|
var countBox = new NumericUpDown
|
||||||
|
{
|
||||||
|
FontSize = 12, Minimum = 0, Maximum = 1000, Increment = 1,
|
||||||
|
Value = lead.LoadCount, MinWidth = 120, FormatString = "0"
|
||||||
|
};
|
||||||
|
|
||||||
|
void Apply()
|
||||||
|
{
|
||||||
|
string type = typeBox.Text?.Trim() ?? "";
|
||||||
|
int count = (int)(countBox.Value ?? 0);
|
||||||
|
foreach (var c in item.Cars)
|
||||||
|
{
|
||||||
|
c.LoadCount = count;
|
||||||
|
c.LoadType = count > 0 && !string.IsNullOrEmpty(type) ? type : c.LoadType;
|
||||||
|
if (count > 0)
|
||||||
|
c.HasVelocity = true; // velocity token must precede loadcount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typeBox.LostFocus += (_, _) => Apply();
|
||||||
|
countBox.ValueChanged += (_, _) => Apply();
|
||||||
|
|
||||||
|
loadsPanel.Children.Add(UnitTitle(item));
|
||||||
|
loadsPanel.Children.Add(LabeledRow(App.Loc["LoadType"], typeBox));
|
||||||
|
loadsPanel.Children.Add(LabeledRow(App.Loc["LoadCount"], countBox));
|
||||||
|
loadsPanel.Children.Add(new TextBlock
|
||||||
|
{
|
||||||
|
Text = App.Loc["LoadHint"], Opacity = 0.6, FontSize = 11, TextWrapping = TextWrapping.Wrap
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cargo names parsed once from data/load_weights.txt (the file the simulator
|
||||||
|
// and the original Starter use), for the load-type suggestions. Each line is
|
||||||
|
// "<name>: <weight>"; only the name (before the colon) is used here.
|
||||||
|
private static List<string>? _loadTypes;
|
||||||
|
private static IReadOnlyList<string> LoadTypes()
|
||||||
|
{
|
||||||
|
if (_loadTypes != null)
|
||||||
|
return _loadTypes;
|
||||||
|
|
||||||
|
var list = new List<string>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string path = Path.Combine("data", "load_weights.txt");
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
foreach (var raw in File.ReadAllLines(path, Encoding.GetEncoding(1250)))
|
||||||
|
{
|
||||||
|
string line = raw.Trim();
|
||||||
|
if (line.Length == 0 || line.StartsWith("#") || line.StartsWith("//"))
|
||||||
|
continue;
|
||||||
|
int colon = line.IndexOf(':');
|
||||||
|
string name = (colon >= 0 ? line[..colon] : line).Trim();
|
||||||
|
if (name.Length > 0)
|
||||||
|
list.Add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { /* missing / unreadable - just no suggestions */ }
|
||||||
|
|
||||||
|
_loadTypes = list.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.OrderBy(s => s, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
return _loadTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------- minis
|
// ------------------------------------------------------------------- minis
|
||||||
@@ -1049,18 +1222,6 @@ public partial class Depot : UserControl
|
|||||||
_searchTimer.Start();
|
_searchTimer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NewConsist_OnClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
// detach from any scenery trainset - this consist is now standalone
|
|
||||||
_editingTrainset = null;
|
|
||||||
AppState.Instance.CurrentTrainset = null;
|
|
||||||
_consist.Clear();
|
|
||||||
_selected = null;
|
|
||||||
_suppress = true;
|
|
||||||
sceneryConsistCombo.SelectedItem = null;
|
|
||||||
_suppress = false;
|
|
||||||
RebuildConsist();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// One entry in the consist: a single vehicle or a locked multi-car unit.
|
// One entry in the consist: a single vehicle or a locked multi-car unit.
|
||||||
|
|||||||
@@ -21,7 +21,8 @@
|
|||||||
<HeaderedContentControl Grid.Column="0" Grid.Row="0" Theme="{StaticResource GroupBox}"
|
<HeaderedContentControl Grid.Column="0" Grid.Row="0" Theme="{StaticResource GroupBox}"
|
||||||
Classes="Compact"
|
Classes="Compact"
|
||||||
Header="{Binding [Sceneries], Source={x:Static starterNg:App.Loc}}">
|
Header="{Binding [Sceneries], Source={x:Static starterNg:App.Loc}}">
|
||||||
<TreeView Name="sceneryList" SelectionChanged="SceneryList_OnSelectionChanged" />
|
<TreeView Name="sceneryList" Classes="Compact"
|
||||||
|
SelectionChanged="SceneryList_OnSelectionChanged" />
|
||||||
</HeaderedContentControl>
|
</HeaderedContentControl>
|
||||||
|
|
||||||
<!-- Centre: trainsets list (compact, fills) + description/weather/timetable tabs -->
|
<!-- Centre: trainsets list (compact, fills) + description/weather/timetable tabs -->
|
||||||
|
|||||||
@@ -68,8 +68,14 @@ public partial class Scenarios : UserControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// refresh the consist preview when the view is shown again (e.g. after
|
// refresh the consist preview when the view is shown again (e.g. after
|
||||||
// editing the consist in the depot)
|
// editing the consist in the depot). Switching tabs only toggles IsVisible
|
||||||
|
// (the view stays in the tree), so listen for that too.
|
||||||
AttachedToVisualTree += (_, _) => RefreshSelectedConsist();
|
AttachedToVisualTree += (_, _) => RefreshSelectedConsist();
|
||||||
|
PropertyChanged += (_, e) =>
|
||||||
|
{
|
||||||
|
if (e.Property == IsVisibleProperty && IsVisible)
|
||||||
|
RefreshSelectedConsist();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SceneryList_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
private void SceneryList_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||||
|
|||||||
@@ -115,6 +115,17 @@
|
|||||||
<String key="Brakes">Brakes</String>
|
<String key="Brakes">Brakes</String>
|
||||||
<String key="Loads">Loads</String>
|
<String key="Loads">Loads</String>
|
||||||
<String key="Coupling">Coupling</String>
|
<String key="Coupling">Coupling</String>
|
||||||
|
<String key="MapConsists">Consists on the scenery</String>
|
||||||
|
<String key="NoSceneryConsists">Select a scenery in the Scenarios tab.</String>
|
||||||
|
<String key="BrakeMode">Brake mode</String>
|
||||||
|
<String key="BrakeLoad">Load setting</String>
|
||||||
|
<String key="BrakeSwitch">Brake switch</String>
|
||||||
|
<String key="None">—</String>
|
||||||
|
<String key="LoadType">Load type</String>
|
||||||
|
<String key="LoadCount">Amount</String>
|
||||||
|
<String key="LoadHint">Set an amount above 0 to load the vehicle.</String>
|
||||||
|
<String key="WagonNumber">Wagon number</String>
|
||||||
|
<String key="Set">Set</String>
|
||||||
<String key="SelectVehicleHint">Select a vehicle in the consist to edit it.</String>
|
<String key="SelectVehicleHint">Select a vehicle in the consist to edit it.</String>
|
||||||
<String key="ComingSoon">Editing options coming soon.</String>
|
<String key="ComingSoon">Editing options coming soon.</String>
|
||||||
<String key="Split">Split</String>
|
<String key="Split">Split</String>
|
||||||
|
|||||||
@@ -115,6 +115,17 @@
|
|||||||
<String key="Brakes">Hamulce</String>
|
<String key="Brakes">Hamulce</String>
|
||||||
<String key="Loads">Ładunki</String>
|
<String key="Loads">Ładunki</String>
|
||||||
<String key="Coupling">Sprzęg</String>
|
<String key="Coupling">Sprzęg</String>
|
||||||
|
<String key="MapConsists">Składy na scenerii</String>
|
||||||
|
<String key="NoSceneryConsists">Wybierz scenerię w zakładce Scenariusze.</String>
|
||||||
|
<String key="BrakeMode">Tryb hamulca</String>
|
||||||
|
<String key="BrakeLoad">Nastawa ładunkowa</String>
|
||||||
|
<String key="BrakeSwitch">Włącznik hamulca</String>
|
||||||
|
<String key="None">—</String>
|
||||||
|
<String key="LoadType">Rodzaj ładunku</String>
|
||||||
|
<String key="LoadCount">Ilość</String>
|
||||||
|
<String key="LoadHint">Ustaw ilość większą od 0, aby załadować pojazd.</String>
|
||||||
|
<String key="WagonNumber">Numer wagonu</String>
|
||||||
|
<String key="Set">Ustaw</String>
|
||||||
<String key="SelectVehicleHint">Wybierz pojazd w składzie, aby go edytować.</String>
|
<String key="SelectVehicleHint">Wybierz pojazd w składzie, aby go edytować.</String>
|
||||||
<String key="ComingSoon">Opcje edycji wkrótce.</String>
|
<String key="ComingSoon">Opcje edycji wkrótce.</String>
|
||||||
<String key="Split">Rozdziel</String>
|
<String key="Split">Rozdziel</String>
|
||||||
|
|||||||
Reference in New Issue
Block a user