| 1 | using System.Text; |
| 2 | |
| 3 | namespace CascadeIDE.Services.ChordNotation; |
| 4 | |
| 5 | /// <summary>Фабрика и общая логика порядка модификаторов (Control → Alt → Shift → Meta).</summary> |
| 6 | public static class ChordNotationRenderer |
| 7 | { |
| 8 | /// <summary>Пробел между шагами последовательности (как во вводе KeyGesture).</summary> |
| 9 | public const char StepSeparator = ' '; |
| 10 | |
| 11 | public static IChordNotationRenderer Windows { get; } = new WindowsChordNotationRenderer(); |
| 12 | |
| 13 | public static IChordNotationRenderer MacSymbols { get; } = new MacSymbolChordNotationRenderer(); |
| 14 | |
| 15 | /// <summary>Третий вид: сегменты для визуальных «клавиш» (<see cref="ChordKeycapStrip"/>).</summary> |
| 16 | public static ChordKeycapSequence BuildKeycapLayout(NormalizedKeySequence? sequence, ChordKeycapLabelFlavor flavor = ChordKeycapLabelFlavor.WindowsWords) => |
| 17 | ChordKeycapLayoutBuilder.Build(sequence, flavor); |
| 18 | |
| 19 | /// <summary>Один аккорд (без последовательности шагов) — для подписи к одному жесту.</summary> |
| 20 | public static string FormatChord(ChordModifierKeys modifiers, string keySymbol, ChordNotationRenderFlavor flavor) => |
| 21 | flavor == ChordNotationRenderFlavor.MacSymbols |
| 22 | ? MacSymbolChordNotationRenderer.RenderChord(modifiers, keySymbol) |
| 23 | : WindowsChordNotationRenderer.RenderChord(modifiers, keySymbol); |
| 24 | } |
| 25 | |
| 26 | public enum ChordNotationRenderFlavor |
| 27 | { |
| 28 | /// <summary>Ctrl+, Alt+, Shift+, Win+ (Meta).</summary> |
| 29 | Windows, |
| 30 | |
| 31 | /// <summary>⌃ ⌥ ⇧ ⌘ и ключ (Meta → ⌘).</summary> |
| 32 | MacSymbols |
| 33 | } |
| 34 | |
| 35 | /// <summary>Ctrl+, Alt+, Shift+, Win+ между частями.</summary> |
| 36 | public sealed class WindowsChordNotationRenderer : IChordNotationRenderer |
| 37 | { |
| 38 | public string Render(NormalizedKeySequence? sequence) |
| 39 | { |
| 40 | if (sequence is null || sequence.Steps.Count == 0) |
| 41 | return ""; |
| 42 | |
| 43 | var sb = new StringBuilder(); |
| 44 | for (var i = 0; i < sequence.Steps.Count; i++) |
| 45 | { |
| 46 | if (i > 0) |
| 47 | sb.Append(ChordNotationRenderer.StepSeparator); |
| 48 | switch (sequence.Steps[i]) |
| 49 | { |
| 50 | case NormalizedChordStep ch: |
| 51 | sb.Append(RenderChord(ch.Modifiers, ch.KeySymbol)); |
| 52 | break; |
| 53 | case NormalizedPlainKeyStep pl: |
| 54 | sb.Append(pl.KeySymbol); |
| 55 | break; |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | return sb.ToString(); |
| 60 | } |
| 61 | |
| 62 | internal static string RenderChord(ChordModifierKeys modifiers, string keySymbol) |
| 63 | { |
| 64 | var sb = new StringBuilder(24); |
| 65 | AppendWindowsModifiers(sb, modifiers); |
| 66 | if (sb.Length > 0) |
| 67 | sb.Append('+'); |
| 68 | sb.Append(keySymbol); |
| 69 | return sb.ToString(); |
| 70 | } |
| 71 | |
| 72 | private static void AppendWindowsModifiers(StringBuilder sb, ChordModifierKeys m) |
| 73 | { |
| 74 | void Part(string label) |
| 75 | { |
| 76 | if (sb.Length > 0) |
| 77 | sb.Append('+'); |
| 78 | sb.Append(label); |
| 79 | } |
| 80 | |
| 81 | if (m.HasFlag(ChordModifierKeys.Control)) |
| 82 | Part("Ctrl"); |
| 83 | if (m.HasFlag(ChordModifierKeys.Alt)) |
| 84 | Part("Alt"); |
| 85 | if (m.HasFlag(ChordModifierKeys.Shift)) |
| 86 | Part("Shift"); |
| 87 | if (m.HasFlag(ChordModifierKeys.Meta)) |
| 88 | Part("Win"); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | /// <summary>⌃⌥⇧⌘ без «+» между глифами; ключ в конце.</summary> |
| 93 | public sealed class MacSymbolChordNotationRenderer : IChordNotationRenderer |
| 94 | { |
| 95 | public string Render(NormalizedKeySequence? sequence) |
| 96 | { |
| 97 | if (sequence is null || sequence.Steps.Count == 0) |
| 98 | return ""; |
| 99 | |
| 100 | var sb = new StringBuilder(); |
| 101 | for (var i = 0; i < sequence.Steps.Count; i++) |
| 102 | { |
| 103 | if (i > 0) |
| 104 | sb.Append(ChordNotationRenderer.StepSeparator); |
| 105 | switch (sequence.Steps[i]) |
| 106 | { |
| 107 | case NormalizedChordStep ch: |
| 108 | sb.Append(RenderChord(ch.Modifiers, ch.KeySymbol)); |
| 109 | break; |
| 110 | case NormalizedPlainKeyStep pl: |
| 111 | sb.Append(pl.KeySymbol); |
| 112 | break; |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | return sb.ToString(); |
| 117 | } |
| 118 | |
| 119 | internal static string RenderChord(ChordModifierKeys modifiers, string keySymbol) |
| 120 | { |
| 121 | var sb = new StringBuilder(16); |
| 122 | if (modifiers.HasFlag(ChordModifierKeys.Control)) |
| 123 | sb.Append('\u2303'); // ⌃ |
| 124 | if (modifiers.HasFlag(ChordModifierKeys.Alt)) |
| 125 | sb.Append('\u2325'); // ⌥ |
| 126 | if (modifiers.HasFlag(ChordModifierKeys.Shift)) |
| 127 | sb.Append('\u21E7'); // ⇧ |
| 128 | if (modifiers.HasFlag(ChordModifierKeys.Meta)) |
| 129 | sb.Append('\u2318'); // ⌘ |
| 130 | sb.Append(keySymbol); |
| 131 | return sb.ToString(); |
| 132 | } |
| 133 | } |
| 134 | |