diff --git a/StarterNG/Views/Depot/CouplerLook.cs b/StarterNG/Views/Depot/CouplerLook.cs
index b81ffcf..df9edc4 100644
--- a/StarterNG/Views/Depot/CouplerLook.cs
+++ b/StarterNG/Views/Depot/CouplerLook.cs
@@ -1,5 +1,8 @@
using System.Collections.Generic;
using System.Linq;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Layout;
using Avalonia.Media;
using Material.Icons;
using StarterNG.Classes;
@@ -28,21 +31,35 @@ public enum CouplerState
}
///
-/// How a coupler is drawn on the consist strip, so its state reads at a glance:
-/// an icon and a bar under it that ties the two cards, both in the state's colour.
-/// Green is left out - on the strip it means selected or turned round.
+/// How a coupler is drawn on the consist strip. Under an icon for the coupler as a
+/// whole runs a bundle of stripes, one per connection in its own colour, so what
+/// is coupled reads at a glance; the coupler editor shows the same colours as its
+/// legend. Green is left out - on the strip it means selected or turned round.
///
public static class CouplerLook
{
private static readonly IBrush Steel = Solid("#C3CBD3");
private static readonly IBrush Dim = Solid("#6B737B");
- private static readonly IBrush Sky = Solid("#4FB8FF");
- private static readonly IBrush Violet = Solid("#B08CFF");
private static readonly IBrush Red = Solid("#FF5A5A");
private static readonly IBrush Amber = Solid("#F0A030");
+ /// One per bit of , in the same order.
+ private static readonly IBrush[] BitBrushes =
+ {
+ Solid("#C3CBD3"), // mechanical - steel
+ Solid("#FF5A5A"), // brake pipe - red
+ Solid("#4FB8FF"), // multiple-unit control - blue
+ Solid("#FFD43B"), // high voltage - yellow
+ Solid("#B08CFF"), // gangway - violet
+ Solid("#FF9F43"), // auxiliary air - orange
+ Solid("#FF6FB5"), // heating - pink
+ Solid("#7A838C") // workshop lock - dark steel
+ };
+
private static IBrush Solid(string color) => new SolidColorBrush(Color.Parse(color));
+ public static IBrush BitBrush(int index) => BitBrushes[index];
+
public static CouplerState StateOf(Coupling coupling, bool trailing)
{
int flags = coupling.AbsFlags;
@@ -67,25 +84,34 @@ public static class CouplerLook
public static IBrush Brush(CouplerState state) => state switch
{
CouplerState.Free => Dim,
- CouplerState.MultipleUnit => Sky,
- CouplerState.Permanent => Violet,
CouplerState.EndSignals => Red,
CouplerState.NoEndSignals => Amber,
_ => Steel
};
- /// The bar under the icon: none at the back, broken where nothing is coupled.
- public static bool HasBar(CouplerState state) => state is not (CouplerState.EndSignals or CouplerState.NoEndSignals);
+ /// The bits that are set, as indexes into .
+ public static IEnumerable SetBits(Coupling coupling) =>
+ Enumerable.Range(0, CouplingBits.BitKeys.Length).Where(i => coupling.Has(1 << i));
- /// Thicker where the vehicles do not part in shunting.
- public static double BarThickness(CouplerState state) => state == CouplerState.Permanent ? 5 : 3;
+ /// A connection's name with its colour beside it, for the editor's checkboxes.
+ public static Control BitLabel(int index) => new StackPanel
+ {
+ Orientation = Orientation.Horizontal,
+ Spacing = 6,
+ Children =
+ {
+ new Border
+ {
+ Width = 10, Height = 10, CornerRadius = new CornerRadius(2),
+ Background = BitBrushes[index], VerticalAlignment = VerticalAlignment.Center
+ },
+ new TextBlock { Text = App.Loc[CouplingBits.BitKeys[index]], VerticalAlignment = VerticalAlignment.Center }
+ }
+ };
/// What the coupler connects, by the names the coupler editor uses.
public static IReadOnlyList Connections(Coupling coupling) =>
- Enumerable.Range(0, CouplingBits.BitKeys.Length)
- .Where(i => coupling.Has(1 << i))
- .Select(i => App.Loc[CouplingBits.BitKeys[i]])
- .ToList();
+ SetBits(coupling).Select(i => App.Loc[CouplingBits.BitKeys[i]]).ToList();
/// The tooltip: the code as the scenery has it, then what it means.
public static string Describe(Coupling coupling, bool trailing)
diff --git a/StarterNG/Views/Depot/VehicleCards.cs b/StarterNG/Views/Depot/VehicleCards.cs
index 22a8dbd..a5ed5d3 100644
--- a/StarterNG/Views/Depot/VehicleCards.cs
+++ b/StarterNG/Views/Depot/VehicleCards.cs
@@ -416,12 +416,11 @@ public sealed class VehicleCards
{
var coupling = Consist.TailCar(item).Coupling;
var icon = new MaterialIcon { Width = 18, Height = 18, HorizontalAlignment = HorizontalAlignment.Center };
- var bar = new Line
+ // Room for all eight, so every icon on the strip sits level.
+ var stripes = new StackPanel
{
- StartPoint = new Point(0, 0),
- EndPoint = new Point(22, 0),
- HorizontalAlignment = HorizontalAlignment.Center,
- StrokeLineCap = PenLineCap.Round
+ Spacing = 1, Height = 23,
+ HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
};
var coupler = new Button
{
@@ -429,20 +428,33 @@ public sealed class VehicleCards
MinWidth = 0,
Cursor = _hand,
VerticalAlignment = VerticalAlignment.Center,
- Content = new StackPanel { Spacing = 4, Children = { icon, bar } }
+ Content = new StackPanel { Spacing = 4, Children = { icon, stripes } }
};
coupler.Classes.Add("Basic");
void Restyle()
{
var state = CouplerLook.StateOf(coupling, trailing);
- var brush = CouplerLook.Brush(state);
icon.Kind = CouplerLook.Icon(state);
- icon.Foreground = brush;
- bar.Opacity = CouplerLook.HasBar(state) ? 1 : 0; // kept in the layout, so every icon sits level
- bar.Stroke = brush;
- bar.StrokeThickness = CouplerLook.BarThickness(state);
- bar.StrokeDashArray = state == CouplerState.Free ? new AvaloniaList { 1.5, 1.5 } : null;
+ icon.Foreground = CouplerLook.Brush(state);
+
+ // A stripe per connection, the way the hoses and cables hang between
+ // the buffers; nothing coupled leaves a broken line.
+ stripes.Children.Clear();
+ foreach (int bit in CouplerLook.SetBits(coupling))
+ stripes.Children.Add(new Border
+ {
+ Width = 22, Height = 2, CornerRadius = new CornerRadius(1),
+ Background = CouplerLook.BitBrush(bit)
+ });
+ if (stripes.Children.Count == 0 && !trailing)
+ stripes.Children.Add(new Line
+ {
+ StartPoint = new Point(0, 0), EndPoint = new Point(22, 0),
+ Stroke = CouplerLook.Brush(state), StrokeThickness = 2,
+ StrokeDashArray = new AvaloniaList { 2, 2 }
+ });
+
ToolTip.SetTip(coupler, CouplerLook.Describe(coupling, trailing));
}
@@ -523,7 +535,7 @@ public sealed class VehicleCards
int bit = 1 << i;
var check = new CheckBox
{
- Content = App.Loc[CouplingBits.BitKeys[i]],
+ Content = CouplerLook.BitLabel(i),
IsChecked = d.Coupling.Has(bit),
FontSize = 12
};
@@ -540,15 +552,8 @@ public sealed class VehicleCards
}
panel.Children.Add(grid);
- var thermo = new CheckBox
- {
- Content = App.Loc["ThermoAmbient"],
- IsChecked = d.Coupling.ThermoDynamic,
- FontSize = 12,
- Margin = new Thickness(0, 2, 0, 0)
- };
- thermo.IsCheckedChanged += (_, _) => d.Coupling.ThermoDynamic = thermo.IsChecked == true;
- panel.Children.Add(thermo);
+ // The ambient coolant flag rides in the coupling token too, but it is the
+ // vehicle's: it sits in the vehicle panel, beside the crew and the number.
return new Border { Padding = new Thickness(8), Child = panel };
}
diff --git a/StarterNG/Views/Depot/VehicleDetails.cs b/StarterNG/Views/Depot/VehicleDetails.cs
index a64c3d8..c170b7a 100644
--- a/StarterNG/Views/Depot/VehicleDetails.cs
+++ b/StarterNG/Views/Depot/VehicleDetails.cs
@@ -196,7 +196,7 @@ public sealed class VehicleDetails
int bit = 1 << i;
var check = new CheckBox
{
- Content = App.Loc[CouplingBits.BitKeys[i]],
+ Content = CouplerLook.BitLabel(i),
IsChecked = d.Coupling.Has(bit),
Classes = { "Checklist" }
};