| 1 | using Avalonia; |
| 2 | using Avalonia.Controls; |
| 3 | using Avalonia.Input; |
| 4 | using Avalonia.VisualTree; |
| 5 | |
| 6 | namespace CascadeIDE.Services; |
| 7 | |
| 8 | /// <summary> |
| 9 | /// Отправить сочетание клавиш в эффективный (под курсором) или указанный по имени контрол. Текст вида "Ctrl+Enter", "Alt+F4". Для ide_send_keys. |
| 10 | /// </summary> |
| 11 | public static class UiControlSendKeys |
| 12 | { |
| 13 | public static string SendKeys(TopLevel topLevel, Visual root, string? controlName, string keysSpec) |
| 14 | { |
| 15 | if (string.IsNullOrWhiteSpace(keysSpec)) |
| 16 | return "Missing keys (e.g. Ctrl+Enter, Alt+F4)."; |
| 17 | |
| 18 | Control? control; |
| 19 | if (!string.IsNullOrWhiteSpace(controlName)) |
| 20 | { |
| 21 | control = root is Window mw |
| 22 | ? UiControlAppearance.FindControlByNameAcrossAllWindows(mw, controlName.Trim()) |
| 23 | : UiControlAppearance.FindControlByName(root, controlName.Trim()); |
| 24 | if (control is null) |
| 25 | return $"Control not found: {controlName}."; |
| 26 | } |
| 27 | else |
| 28 | { |
| 29 | control = UiPointerClientPosition.TryGetControlUnderPointer(topLevel); |
| 30 | if (control is null) |
| 31 | return "No control under cursor. Specify name or focus the target first."; |
| 32 | } |
| 33 | |
| 34 | if (!TryParseKeys(keysSpec.Trim(), out var key, out var modifiers, out var parseError)) |
| 35 | return parseError ?? "Invalid keys."; |
| 36 | |
| 37 | control.Focus(); |
| 38 | var keyDown = new KeyEventArgs |
| 39 | { |
| 40 | RoutedEvent = InputElement.KeyDownEvent, |
| 41 | Source = control, |
| 42 | Key = key, |
| 43 | KeyModifiers = modifiers |
| 44 | }; |
| 45 | var keyUp = new KeyEventArgs |
| 46 | { |
| 47 | RoutedEvent = InputElement.KeyUpEvent, |
| 48 | Source = control, |
| 49 | Key = key, |
| 50 | KeyModifiers = modifiers |
| 51 | }; |
| 52 | control.RaiseEvent(keyDown); |
| 53 | control.RaiseEvent(keyUp); |
| 54 | return "OK"; |
| 55 | } |
| 56 | |
| 57 | private static bool TryParseKeys(string spec, out Key key, out KeyModifiers modifiers, out string? error) |
| 58 | { |
| 59 | modifiers = KeyModifiers.None; |
| 60 | key = Key.None; |
| 61 | error = ""; |
| 62 | |
| 63 | var parts = spec.Split('+', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); |
| 64 | if (parts.Length == 0) |
| 65 | { |
| 66 | error = "Empty key spec."; |
| 67 | return false; |
| 68 | } |
| 69 | error = null; |
| 70 | |
| 71 | for (var i = 0; i < parts.Length - 1; i++) |
| 72 | { |
| 73 | var mod = parts[i].ToLowerInvariant(); |
| 74 | if (mod is "ctrl" or "control") |
| 75 | modifiers |= KeyModifiers.Control; |
| 76 | else if (mod is "alt") |
| 77 | modifiers |= KeyModifiers.Alt; |
| 78 | else if (mod is "shift") |
| 79 | modifiers |= KeyModifiers.Shift; |
| 80 | else if (mod is "meta" or "win" or "windows") |
| 81 | modifiers |= KeyModifiers.Meta; |
| 82 | else |
| 83 | { |
| 84 | error = $"Unknown modifier: {parts[i]}."; |
| 85 | return false; |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | var keyPart = parts[^1]; |
| 90 | var keyName = keyPart.Length == 1 ? keyPart.ToUpperInvariant() : keyPart.ToUpperInvariant().Replace(" ", ""); |
| 91 | if (!Enum.TryParse<Key>(keyName, true, out key)) |
| 92 | { |
| 93 | error = $"Unknown key: {keyPart}. Use e.g. Enter, Tab, F4, A."; |
| 94 | return false; |
| 95 | } |
| 96 | |
| 97 | return true; |
| 98 | } |
| 99 | |
| 100 | } |
| 101 | |