Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

108 wiersze
4.7 KiB

  1. // This code is distributed under MIT license.
  2. // Copyright (c) 2010-2018 George Mamaladze
  3. // See license.txt or https://mit-license.org/
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using Gma.System.MouseKeyHook.Implementation;
  8. namespace Gma.System.MouseKeyHook
  9. {
  10. /// <summary>
  11. /// Extension methods to detect key combinations
  12. /// </summary>
  13. public static class KeyCombinationExtensions
  14. {
  15. /// <summary>
  16. /// Detects a key or key combination and triggers the corresponding action.
  17. /// </summary>
  18. /// <param name="source">
  19. /// An instance of Global or Application hook. Use <see cref="Hook.GlobalEvents" /> or <see cref="Hook.AppEvents" /> to
  20. /// create it.
  21. /// </param>
  22. /// <param name="map">
  23. /// This map contains the list of key combinations mapped to corresponding actions. You can use a dictionary initilizer
  24. /// to easily create it.
  25. /// Whenever a listed combination will be detected a corresponding action will be triggered.
  26. /// </param>
  27. /// <param name="reset">
  28. /// This optional action will be executed when some key was pressed but it was not part of any wanted combinations.
  29. /// </param>
  30. public static void OnCombination(this IKeyboardEvents source,
  31. IEnumerable<KeyValuePair<Combination, Action>> map, Action reset = null)
  32. {
  33. var watchlists = map.GroupBy(k => k.Key.TriggerKey)
  34. .ToDictionary(g => g.Key, g => g.ToArray());
  35. source.KeyDown += (sender, e) =>
  36. {
  37. KeyValuePair<Combination, Action>[] element;
  38. var found = watchlists.TryGetValue(e.KeyCode, out element);
  39. if (!found)
  40. {
  41. reset?.Invoke();
  42. return;
  43. }
  44. var state = KeyboardState.GetCurrent();
  45. var action = reset;
  46. var maxLength = 0;
  47. foreach (var current in element)
  48. {
  49. var matches = current.Key.Chord.All(state.IsDown);
  50. if (!matches) continue;
  51. if (maxLength > current.Key.ChordLength) continue;
  52. maxLength = current.Key.ChordLength;
  53. action = current.Value;
  54. }
  55. action?.Invoke();
  56. };
  57. }
  58. /// <summary>
  59. /// Detects a key or key combination sequence and triggers the corresponding action.
  60. /// </summary>
  61. /// <param name="source">
  62. /// An instance of Global or Application hook. Use <see cref="Hook.GlobalEvents" /> or
  63. /// <see cref="Hook.AppEvents" /> to create it.
  64. /// </param>
  65. /// <param name="map">
  66. /// This map contains the list of sequences mapped to corresponding actions. You can use a dictionary initilizer to
  67. /// easily create it.
  68. /// Whenever a listed sequnce will be detected a corresponding action will be triggered. If two or more sequences match
  69. /// the longest one will be used.
  70. /// Example: sequences may A,B,C and B,C might be detected simultanously if user pressed first A then B then C. In this
  71. /// case only action corresponding
  72. /// to 'A,B,C' will be triggered.
  73. /// </param>
  74. public static void OnSequence(this IKeyboardEvents source, IEnumerable<KeyValuePair<Sequence, Action>> map)
  75. {
  76. var actBySeq = map.ToArray();
  77. var endsWith = new Func<Queue<Combination>, Sequence, bool>((chords, sequence) =>
  78. {
  79. var skipCount = chords.Count - sequence.Length;
  80. return skipCount >= 0 && chords.Skip(skipCount).SequenceEqual(sequence);
  81. });
  82. var max = actBySeq.Select(p => p.Key).Max(c => c.Length);
  83. var min = actBySeq.Select(p => p.Key).Min(c => c.Length);
  84. var buffer = new Queue<Combination>(max);
  85. var wrapMap = actBySeq.SelectMany(p => p.Key).Select(c => new KeyValuePair<Combination, Action>(c, () =>
  86. {
  87. buffer.Enqueue(c);
  88. if (buffer.Count > max) buffer.Dequeue();
  89. if (buffer.Count < min) return;
  90. //Invoke action corresponding to the longest matching sequence
  91. actBySeq
  92. .Where(pair => endsWith(buffer, pair.Key))
  93. .OrderBy(pair => pair.Key.Length)
  94. .Select(pair => pair.Value)
  95. .LastOrDefault()
  96. ?.Invoke();
  97. }));
  98. OnCombination(source, wrapMap, buffer.Clear);
  99. }
  100. }
  101. }