From 526ff91b3bdd21dd6d4cfd1b0df5d02dad5ef363 Mon Sep 17 00:00:00 2001 From: maj00r Date: Sun, 4 Oct 2026 19:09:57 +0200 Subject: [PATCH] Draw depot couplers as an icon and a link bar in the state's colour Each state gets its own icon and colour, and a bar under it ties the two cards: broken when free, thicker when permanent, absent at the back. Green is gone - on the strip it already means selected or turned round. --- StarterNG/Views/Depot/CouplerLook.cs | 47 +++++++++++++++++---------- StarterNG/Views/Depot/VehicleCards.cs | 34 +++++++++---------- 2 files changed, 47 insertions(+), 34 deletions(-) diff --git a/StarterNG/Views/Depot/CouplerLook.cs b/StarterNG/Views/Depot/CouplerLook.cs index 5b733de..b81ffcf 100644 --- a/StarterNG/Views/Depot/CouplerLook.cs +++ b/StarterNG/Views/Depot/CouplerLook.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Media; +using Material.Icons; using StarterNG.Classes; namespace StarterNG.Views; @@ -26,14 +27,21 @@ public enum CouplerState NoEndSignals } -/// 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, 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. +/// public static class CouplerLook { - private static readonly IBrush Dim = new SolidColorBrush(Color.Parse("#808080")); - private static readonly IBrush Green = new SolidColorBrush(Color.Parse("#41C400")); - private static readonly IBrush Blue = new SolidColorBrush(Color.Parse("#4AA3FF")); - private static readonly IBrush Red = new SolidColorBrush(Color.Parse("#E5484D")); - private static readonly IBrush Amber = new SolidColorBrush(Color.Parse("#E0A030")); + 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"); + + private static IBrush Solid(string color) => new SolidColorBrush(Color.Parse(color)); public static CouplerState StateOf(Coupling coupling, bool trailing) { @@ -47,26 +55,31 @@ public static class CouplerLook return (flags & Coupling.ControlMU) != 0 ? CouplerState.MultipleUnit : CouplerState.Coupled; } - // Box drawing and a plain disc: Windows draws no colour emoji for these. - public static string Glyph(CouplerState state) => state switch + public static MaterialIconKind Icon(CouplerState state) => state switch { - CouplerState.Free => "╎", - CouplerState.Permanent => "═", - CouplerState.EndSignals => "●", - _ => "≣" + CouplerState.Free => MaterialIconKind.LinkVariantOff, + CouplerState.Permanent => MaterialIconKind.LinkLock, + CouplerState.EndSignals => MaterialIconKind.RecordCircle, + CouplerState.NoEndSignals => MaterialIconKind.AlertCircleOutline, + _ => MaterialIconKind.LinkVariant }; - /// The colour of the glyph; null keeps the ordinary text colour. - public static IBrush? Brush(CouplerState state) => state switch + public static IBrush Brush(CouplerState state) => state switch { CouplerState.Free => Dim, - CouplerState.MultipleUnit => Green, - CouplerState.Permanent => Blue, + CouplerState.MultipleUnit => Sky, + CouplerState.Permanent => Violet, CouplerState.EndSignals => Red, CouplerState.NoEndSignals => Amber, - _ => null + _ => 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); + + /// Thicker where the vehicles do not part in shunting. + public static double BarThickness(CouplerState state) => state == CouplerState.Permanent ? 5 : 3; + /// What the coupler connects, by the names the coupler editor uses. public static IReadOnlyList Connections(Coupling coupling) => Enumerable.Range(0, CouplingBits.BitKeys.Length) diff --git a/StarterNG/Views/Depot/VehicleCards.cs b/StarterNG/Views/Depot/VehicleCards.cs index 08a0dd5..a43d2cc 100644 --- a/StarterNG/Views/Depot/VehicleCards.cs +++ b/StarterNG/Views/Depot/VehicleCards.cs @@ -2,7 +2,9 @@ using System; using System.Collections.Generic; using System.Linq; using Avalonia; +using Avalonia.Collections; using Avalonia.Controls; +using Avalonia.Controls.Shapes; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Media; @@ -413,36 +415,34 @@ public sealed class VehicleCards public Control BuildCoupler(ConsistItem item, bool trailing = false) { var coupling = Consist.TailCar(item).Coupling; - var glyph = new TextBlock + var icon = new MaterialIcon { Width = 18, Height = 18, HorizontalAlignment = HorizontalAlignment.Center }; + var bar = new Line { - FontSize = 18, - VerticalAlignment = VerticalAlignment.Center, - HorizontalAlignment = HorizontalAlignment.Center + StartPoint = new Point(0, 0), + EndPoint = new Point(22, 0), + HorizontalAlignment = HorizontalAlignment.Center, + StrokeLineCap = PenLineCap.Round }; var coupler = new Button { - Padding = new Thickness(4, 6, 4, 6), + Padding = new Thickness(3, 6, 3, 6), MinWidth = 0, Cursor = _hand, VerticalAlignment = VerticalAlignment.Center, - Content = glyph + Content = new StackPanel { Spacing = 4, Children = { icon, bar } } }; coupler.Classes.Add("Basic"); void Restyle() { var state = CouplerLook.StateOf(coupling, trailing); - glyph.Text = CouplerLook.Glyph(state); - if (CouplerLook.Brush(state) is { } brush) - { - glyph.Foreground = brush; - glyph.Opacity = 1; - } - else - { - glyph.ClearValue(TextBlock.ForegroundProperty); - glyph.Opacity = 0.7; - } + 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; ToolTip.SetTip(coupler, CouplerLook.Describe(coupling, trailing)); }