| 1 | #nullable enable |
| 2 | using Avalonia.Input; |
| 3 | |
| 4 | namespace CascadeIDE.Services; |
| 5 | |
| 6 | /// <summary> |
| 7 | /// Сопоставление <see cref="KeyGesture"/> с <see cref="KeyEventArgs"/> с учётом раскладки и лишних битов в <see cref="KeyEventArgs.KeyModifiers"/> |
| 8 | /// (в т.ч. Avalonia 11+): встроенный <see cref="KeyGesture.Matches(KeyEventArgs)"/> требует полного совпадения модификаторов и совпадения <see cref="KeyEventArgs.Key"/> с жестом. |
| 9 | /// Для буквенных хоткеев (Ctrl+Q, Ctrl+K, …) дополнительно используется <see cref="PhysicalKeyExtensions.ToQwertyKey(Avalonia.Input.PhysicalKey)"/>, чтобы позиция на QWERTY совпадала с жестом при нелатинской раскладке. |
| 10 | /// </summary> |
| 11 | public static class KeyGestureChordMatching |
| 12 | { |
| 13 | private const KeyModifiers ChordModifierMask = |
| 14 | KeyModifiers.Control | KeyModifiers.Shift | KeyModifiers.Alt | KeyModifiers.Meta; |
| 15 | |
| 16 | public static KeyModifiers NormalizeChordModifiers(KeyModifiers m) => m & ChordModifierMask; |
| 17 | |
| 18 | /// <summary>Та же логика, что у <see cref="KeyGesture"/> для цифровой клавиатуры.</summary> |
| 19 | public static Key NormalizeNumPadAlias(Key key) => |
| 20 | key switch |
| 21 | { |
| 22 | Key.Add => Key.OemPlus, |
| 23 | Key.Subtract => Key.OemMinus, |
| 24 | Key.Decimal => Key.OemPeriod, |
| 25 | _ => key, |
| 26 | }; |
| 27 | |
| 28 | /// <summary> |
| 29 | /// Сначала строгий <see cref="KeyGesture.Matches(KeyEventArgs)"/>; иначе совпадение по нормализованным модификаторам |
| 30 | /// и клавише (включая non-Latin: та же физическая K, что и у жеста <c>Key.K</c>). |
| 31 | /// </summary> |
| 32 | public static bool Matches(KeyGesture gesture, KeyEventArgs e) |
| 33 | { |
| 34 | if (gesture.Matches(e)) |
| 35 | return true; |
| 36 | |
| 37 | if (NormalizeChordModifiers(e.KeyModifiers) != NormalizeChordModifiers(gesture.KeyModifiers)) |
| 38 | return false; |
| 39 | |
| 40 | if (NormalizeNumPadAlias(e.Key) == NormalizeNumPadAlias(gesture.Key)) |
| 41 | return true; |
| 42 | |
| 43 | // Нелатинские раскладки: e.Key — по текущей раскладке, жест в TOML — как на QWERTY (Ctrl+Q, Ctrl+K, …). |
| 44 | // См. KeyEventArgs.Key / PhysicalKey в документации Avalonia; ToQwertyKey — позиция на US QWERTY → Key. |
| 45 | var qwertyKey = e.PhysicalKey.ToQwertyKey(); |
| 46 | if (qwertyKey != Key.None && |
| 47 | NormalizeNumPadAlias(qwertyKey) == NormalizeNumPadAlias(gesture.Key)) |
| 48 | return true; |
| 49 | |
| 50 | return false; |
| 51 | } |
| 52 | } |
| 53 | |