mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-10-16 23:49:19 +02:00
Show what a coupler connects as a stripe per connection
Under the coupler's icon runs a stripe for each connection in its own colour - brake red, multiple-unit blue, high voltage yellow and so on - so the strip tells the kinds of coupling apart, not just coupled from free. The editor's checkboxes carry the same colours as the legend. The ambient coolant flag leaves the coupler flyout: it is the vehicle's and already sits in the vehicle panel, beside the crew and the wagon number.
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Layout;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Material.Icons;
|
using Material.Icons;
|
||||||
using StarterNG.Classes;
|
using StarterNG.Classes;
|
||||||
@@ -28,21 +31,35 @@ public enum CouplerState
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How a coupler is drawn on the consist strip, so its state reads at a glance:
|
/// How a coupler is drawn on the consist strip. Under an icon for the coupler as a
|
||||||
/// an icon and a bar under it that ties the two cards, both in the state's colour.
|
/// whole runs a bundle of stripes, one per connection in its own colour, so what
|
||||||
/// Green is left out - on the strip it means selected or turned round.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class CouplerLook
|
public static class CouplerLook
|
||||||
{
|
{
|
||||||
private static readonly IBrush Steel = Solid("#C3CBD3");
|
private static readonly IBrush Steel = Solid("#C3CBD3");
|
||||||
private static readonly IBrush Dim = Solid("#6B737B");
|
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 Red = Solid("#FF5A5A");
|
||||||
private static readonly IBrush Amber = Solid("#F0A030");
|
private static readonly IBrush Amber = Solid("#F0A030");
|
||||||
|
|
||||||
|
/// <summary>One per bit of <see cref="CouplingBits.BitKeys"/>, in the same order.</summary>
|
||||||
|
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));
|
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)
|
public static CouplerState StateOf(Coupling coupling, bool trailing)
|
||||||
{
|
{
|
||||||
int flags = coupling.AbsFlags;
|
int flags = coupling.AbsFlags;
|
||||||
@@ -67,25 +84,34 @@ public static class CouplerLook
|
|||||||
public static IBrush Brush(CouplerState state) => state switch
|
public static IBrush Brush(CouplerState state) => state switch
|
||||||
{
|
{
|
||||||
CouplerState.Free => Dim,
|
CouplerState.Free => Dim,
|
||||||
CouplerState.MultipleUnit => Sky,
|
|
||||||
CouplerState.Permanent => Violet,
|
|
||||||
CouplerState.EndSignals => Red,
|
CouplerState.EndSignals => Red,
|
||||||
CouplerState.NoEndSignals => Amber,
|
CouplerState.NoEndSignals => Amber,
|
||||||
_ => Steel
|
_ => Steel
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>The bar under the icon: none at the back, broken where nothing is coupled.</summary>
|
/// <summary>The bits that are set, as indexes into <see cref="CouplingBits.BitKeys"/>.</summary>
|
||||||
public static bool HasBar(CouplerState state) => state is not (CouplerState.EndSignals or CouplerState.NoEndSignals);
|
public static IEnumerable<int> SetBits(Coupling coupling) =>
|
||||||
|
Enumerable.Range(0, CouplingBits.BitKeys.Length).Where(i => coupling.Has(1 << i));
|
||||||
|
|
||||||
/// <summary>Thicker where the vehicles do not part in shunting.</summary>
|
/// <summary>A connection's name with its colour beside it, for the editor's checkboxes.</summary>
|
||||||
public static double BarThickness(CouplerState state) => state == CouplerState.Permanent ? 5 : 3;
|
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 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>What the coupler connects, by the names the coupler editor uses.</summary>
|
/// <summary>What the coupler connects, by the names the coupler editor uses.</summary>
|
||||||
public static IReadOnlyList<string> Connections(Coupling coupling) =>
|
public static IReadOnlyList<string> Connections(Coupling coupling) =>
|
||||||
Enumerable.Range(0, CouplingBits.BitKeys.Length)
|
SetBits(coupling).Select(i => App.Loc[CouplingBits.BitKeys[i]]).ToList();
|
||||||
.Where(i => coupling.Has(1 << i))
|
|
||||||
.Select(i => App.Loc[CouplingBits.BitKeys[i]])
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
/// <summary>The tooltip: the code as the scenery has it, then what it means.</summary>
|
/// <summary>The tooltip: the code as the scenery has it, then what it means.</summary>
|
||||||
public static string Describe(Coupling coupling, bool trailing)
|
public static string Describe(Coupling coupling, bool trailing)
|
||||||
|
|||||||
@@ -416,12 +416,11 @@ public sealed class VehicleCards
|
|||||||
{
|
{
|
||||||
var coupling = Consist.TailCar(item).Coupling;
|
var coupling = Consist.TailCar(item).Coupling;
|
||||||
var icon = new MaterialIcon { Width = 18, Height = 18, HorizontalAlignment = HorizontalAlignment.Center };
|
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),
|
Spacing = 1, Height = 23,
|
||||||
EndPoint = new Point(22, 0),
|
HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top
|
||||||
HorizontalAlignment = HorizontalAlignment.Center,
|
|
||||||
StrokeLineCap = PenLineCap.Round
|
|
||||||
};
|
};
|
||||||
var coupler = new Button
|
var coupler = new Button
|
||||||
{
|
{
|
||||||
@@ -429,20 +428,33 @@ public sealed class VehicleCards
|
|||||||
MinWidth = 0,
|
MinWidth = 0,
|
||||||
Cursor = _hand,
|
Cursor = _hand,
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
Content = new StackPanel { Spacing = 4, Children = { icon, bar } }
|
Content = new StackPanel { Spacing = 4, Children = { icon, stripes } }
|
||||||
};
|
};
|
||||||
coupler.Classes.Add("Basic");
|
coupler.Classes.Add("Basic");
|
||||||
|
|
||||||
void Restyle()
|
void Restyle()
|
||||||
{
|
{
|
||||||
var state = CouplerLook.StateOf(coupling, trailing);
|
var state = CouplerLook.StateOf(coupling, trailing);
|
||||||
var brush = CouplerLook.Brush(state);
|
|
||||||
icon.Kind = CouplerLook.Icon(state);
|
icon.Kind = CouplerLook.Icon(state);
|
||||||
icon.Foreground = brush;
|
icon.Foreground = CouplerLook.Brush(state);
|
||||||
bar.Opacity = CouplerLook.HasBar(state) ? 1 : 0; // kept in the layout, so every icon sits level
|
|
||||||
bar.Stroke = brush;
|
// A stripe per connection, the way the hoses and cables hang between
|
||||||
bar.StrokeThickness = CouplerLook.BarThickness(state);
|
// the buffers; nothing coupled leaves a broken line.
|
||||||
bar.StrokeDashArray = state == CouplerState.Free ? new AvaloniaList<double> { 1.5, 1.5 } : null;
|
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<double> { 2, 2 }
|
||||||
|
});
|
||||||
|
|
||||||
ToolTip.SetTip(coupler, CouplerLook.Describe(coupling, trailing));
|
ToolTip.SetTip(coupler, CouplerLook.Describe(coupling, trailing));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,7 +535,7 @@ public sealed class VehicleCards
|
|||||||
int bit = 1 << i;
|
int bit = 1 << i;
|
||||||
var check = new CheckBox
|
var check = new CheckBox
|
||||||
{
|
{
|
||||||
Content = App.Loc[CouplingBits.BitKeys[i]],
|
Content = CouplerLook.BitLabel(i),
|
||||||
IsChecked = d.Coupling.Has(bit),
|
IsChecked = d.Coupling.Has(bit),
|
||||||
FontSize = 12
|
FontSize = 12
|
||||||
};
|
};
|
||||||
@@ -540,15 +552,8 @@ public sealed class VehicleCards
|
|||||||
}
|
}
|
||||||
panel.Children.Add(grid);
|
panel.Children.Add(grid);
|
||||||
|
|
||||||
var thermo = new CheckBox
|
// 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.
|
||||||
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);
|
|
||||||
|
|
||||||
return new Border { Padding = new Thickness(8), Child = panel };
|
return new Border { Padding = new Thickness(8), Child = panel };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ public sealed class VehicleDetails
|
|||||||
int bit = 1 << i;
|
int bit = 1 << i;
|
||||||
var check = new CheckBox
|
var check = new CheckBox
|
||||||
{
|
{
|
||||||
Content = App.Loc[CouplingBits.BitKeys[i]],
|
Content = CouplerLook.BitLabel(i),
|
||||||
IsChecked = d.Coupling.Has(bit),
|
IsChecked = d.Coupling.Has(bit),
|
||||||
Classes = { "Checklist" }
|
Classes = { "Checklist" }
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user