| 1 | using System.Globalization; |
| 2 | using Avalonia; |
| 3 | using Avalonia.Media; |
| 4 | using static CascadeIDE.Cockpit.PrimitivesKit.CockpitPrimitivesPalette.Annunciator; |
| 5 | |
| 6 | namespace CascadeIDE.Cockpit.PrimitivesKit; |
| 7 | |
| 8 | /// <summary> |
| 9 | /// Task cockpit: блокировка сплиттеров (мелодия/бренд TOL; на линзе GND = «на земле» можно двигать границы, IN AIR = зафиксировано) в том же визуальном языке, что <see cref="CommandArmedStripLampFace"/>. |
| 10 | /// </summary> |
| 11 | public readonly record struct WorkspaceSplittersTolLampFace(bool SplittersLocked) |
| 12 | { |
| 13 | /// <summary>true = IN AIR (заблокировано), false = ON GND (сплиттеры можно двигать).</summary> |
| 14 | public void Draw(DrawingContext context, Rect outerRect) |
| 15 | { |
| 16 | AnnunciatorLampChrome.DrawCellHousing(context, outerRect); |
| 17 | |
| 18 | var face = AnnunciatorLampChrome.ComputeFaceSquare(outerRect); |
| 19 | |
| 20 | var faceBase = new LinearGradientBrush |
| 21 | { |
| 22 | StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), |
| 23 | EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), |
| 24 | GradientStops = |
| 25 | { |
| 26 | new GradientStop(OffLensTop, 0), |
| 27 | new GradientStop(OffLensBottom, 1), |
| 28 | }, |
| 29 | }; |
| 30 | context.DrawRectangle(faceBase, new Pen(new SolidColorBrush(OffLensBorder), 0.75), face); |
| 31 | |
| 32 | // Полоса чуть ниже, чем у CMD, чтобы над ней осталась вертикаль на две строки (7pt в две линии не влезали → текст не рисовался). |
| 33 | var barH = Math.Max(5.0, face.Height * 0.22); |
| 34 | var padX = Math.Max(2.0, face.Width * 0.08); |
| 35 | var barY = face.Bottom - barH - 2; |
| 36 | var barRect = new Rect(face.X + padX, barY, face.Width - 2 * padX, barH); |
| 37 | var textBandBottomY = barRect.Y - 2; |
| 38 | |
| 39 | if (SplittersLocked) |
| 40 | { |
| 41 | var g = CommandArmedStripGreen; |
| 42 | var gTop = AnnunciatorLampChrome.Lighten(g, 0.18); |
| 43 | var gBot = AnnunciatorLampChrome.Darken(g, 0.22); |
| 44 | var barBrush = new LinearGradientBrush |
| 45 | { |
| 46 | StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), |
| 47 | EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), |
| 48 | GradientStops = |
| 49 | { |
| 50 | new GradientStop(gTop, 0), |
| 51 | new GradientStop(g, 0.5), |
| 52 | new GradientStop(gBot, 1), |
| 53 | }, |
| 54 | }; |
| 55 | context.DrawRectangle(barBrush, new Pen(Brushes.Black, 0.5), barRect); |
| 56 | DrawTolTwoLineLabelInFace( |
| 57 | context, |
| 58 | face, |
| 59 | textBandBottomY, |
| 60 | "IN", |
| 61 | "AIR", |
| 62 | Brushes.White, |
| 63 | new SolidColorBrush(OutlinedTextStrokeLit)); |
| 64 | } |
| 65 | else |
| 66 | { |
| 67 | var r = Color.Parse("#C62828"); |
| 68 | var rTop = AnnunciatorLampChrome.Lighten(r, 0.12); |
| 69 | var rBot = AnnunciatorLampChrome.Darken(r, 0.2); |
| 70 | var barBrush = new LinearGradientBrush |
| 71 | { |
| 72 | StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative), |
| 73 | EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative), |
| 74 | GradientStops = |
| 75 | { |
| 76 | new GradientStop(rTop, 0), |
| 77 | new GradientStop(r, 0.5), |
| 78 | new GradientStop(rBot, 1), |
| 79 | }, |
| 80 | }; |
| 81 | context.DrawRectangle(barBrush, new Pen(Brushes.Black, 0.5), barRect); |
| 82 | var amber = new SolidColorBrush(CommandArmedLabelAmber); |
| 83 | var stroke = new SolidColorBrush(OutlinedTextStrokeLit); |
| 84 | DrawTolTwoLineLabelInFace( |
| 85 | context, |
| 86 | face, |
| 87 | textBandBottomY, |
| 88 | "ON", |
| 89 | "GND", |
| 90 | amber, |
| 91 | stroke); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | /// <summary>Кегль подбирается по вертикали, чтобы в узкой зоне над полосой текст не исчезал целиком.</summary> |
| 96 | private static void DrawTolTwoLineLabelInFace( |
| 97 | DrawingContext context, |
| 98 | Rect face, |
| 99 | double labelBottomY, |
| 100 | string line1, |
| 101 | string line2, |
| 102 | IBrush fillBrush, |
| 103 | IBrush strokeBrush) |
| 104 | { |
| 105 | if (labelBottomY <= face.Y + 2) |
| 106 | return; |
| 107 | |
| 108 | const double lineGap = 0.4; |
| 109 | var bandTop = face.Y + 2; |
| 110 | var available = labelBottomY - bandTop; |
| 111 | if (available <= 0) |
| 112 | return; |
| 113 | |
| 114 | ReadOnlySpan<double> trySizes = [5.5, 5.0, 4.5, 4.0, 3.5]; |
| 115 | foreach (var fontSize in trySizes) |
| 116 | { |
| 117 | var (ft1Fill, ft1Stroke, ft2Fill, ft2Stroke) = BuildTolLineFormatted( |
| 118 | line1, line2, fontSize, fillBrush, strokeBrush); |
| 119 | var totalH = ft1Fill.Height + lineGap + ft2Fill.Height; |
| 120 | if (totalH > available) |
| 121 | continue; |
| 122 | |
| 123 | var y = bandTop + (available - totalH) / 2; |
| 124 | var x1 = face.X + (face.Width - ft1Fill.Width) / 2; |
| 125 | AnnunciatorLampChrome.DrawOutlinedText(context, ft1Stroke, ft1Fill, new Point(x1, y)); |
| 126 | y += ft1Fill.Height + lineGap; |
| 127 | var x2 = face.X + (face.Width - ft2Fill.Width) / 2; |
| 128 | AnnunciatorLampChrome.DrawOutlinedText(context, ft2Stroke, ft2Fill, new Point(x2, y)); |
| 129 | return; |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | private static (FormattedText Ft1Fill, FormattedText Ft1Stroke, FormattedText Ft2Fill, FormattedText Ft2Stroke) BuildTolLineFormatted( |
| 134 | string line1, |
| 135 | string line2, |
| 136 | double fontSize, |
| 137 | IBrush fillBrush, |
| 138 | IBrush strokeBrush) |
| 139 | { |
| 140 | var ft1Fill = new FormattedText( |
| 141 | line1, |
| 142 | CultureInfo.InvariantCulture, |
| 143 | FlowDirection.LeftToRight, |
| 144 | AnnunciatorLampChrome.LabelTypeface, |
| 145 | fontSize, |
| 146 | fillBrush); |
| 147 | var ft1Stroke = new FormattedText( |
| 148 | line1, |
| 149 | CultureInfo.InvariantCulture, |
| 150 | FlowDirection.LeftToRight, |
| 151 | AnnunciatorLampChrome.LabelTypeface, |
| 152 | fontSize, |
| 153 | strokeBrush); |
| 154 | var ft2Fill = new FormattedText( |
| 155 | line2, |
| 156 | CultureInfo.InvariantCulture, |
| 157 | FlowDirection.LeftToRight, |
| 158 | AnnunciatorLampChrome.LabelTypeface, |
| 159 | fontSize, |
| 160 | fillBrush); |
| 161 | var ft2Stroke = new FormattedText( |
| 162 | line2, |
| 163 | CultureInfo.InvariantCulture, |
| 164 | FlowDirection.LeftToRight, |
| 165 | AnnunciatorLampChrome.LabelTypeface, |
| 166 | fontSize, |
| 167 | strokeBrush); |
| 168 | return (ft1Fill, ft1Stroke, ft2Fill, ft2Stroke); |
| 169 | } |
| 170 | } |
| 171 | |