|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- // This code is distributed under MIT license.
- // Copyright (c) 2010-2018 George Mamaladze
- // See license.txt or https://mit-license.org/
-
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using Gma.System.MouseKeyHook.Implementation;
-
- namespace Gma.System.MouseKeyHook
- {
- /// <summary>
- /// Extension methods to detect key combinations
- /// </summary>
- public static class KeyCombinationExtensions
- {
- /// <summary>
- /// Detects a key or key combination and triggers the corresponding action.
- /// </summary>
- /// <param name="source">
- /// An instance of Global or Application hook. Use <see cref="Hook.GlobalEvents" /> or <see cref="Hook.AppEvents" /> to
- /// create it.
- /// </param>
- /// <param name="map">
- /// This map contains the list of key combinations mapped to corresponding actions. You can use a dictionary initilizer
- /// to easily create it.
- /// Whenever a listed combination will be detected a corresponding action will be triggered.
- /// </param>
- /// <param name="reset">
- /// This optional action will be executed when some key was pressed but it was not part of any wanted combinations.
- /// </param>
- public static void OnCombination(this IKeyboardEvents source,
- IEnumerable<KeyValuePair<Combination, Action>> map, Action reset = null)
- {
- var watchlists = map.GroupBy(k => k.Key.TriggerKey)
- .ToDictionary(g => g.Key, g => g.ToArray());
- source.KeyDown += (sender, e) =>
- {
- KeyValuePair<Combination, Action>[] element;
- var found = watchlists.TryGetValue(e.KeyCode, out element);
- if (!found)
- {
- reset?.Invoke();
- return;
- }
- var state = KeyboardState.GetCurrent();
- var action = reset;
- var maxLength = 0;
- foreach (var current in element)
- {
- var matches = current.Key.Chord.All(state.IsDown);
- if (!matches) continue;
- if (maxLength > current.Key.ChordLength) continue;
- maxLength = current.Key.ChordLength;
- action = current.Value;
- }
- action?.Invoke();
- };
- }
-
-
- /// <summary>
- /// Detects a key or key combination sequence and triggers the corresponding action.
- /// </summary>
- /// <param name="source">
- /// An instance of Global or Application hook. Use <see cref="Hook.GlobalEvents" /> or
- /// <see cref="Hook.AppEvents" /> to create it.
- /// </param>
- /// <param name="map">
- /// This map contains the list of sequences mapped to corresponding actions. You can use a dictionary initilizer to
- /// easily create it.
- /// Whenever a listed sequnce will be detected a corresponding action will be triggered. If two or more sequences match
- /// the longest one will be used.
- /// Example: sequences may A,B,C and B,C might be detected simultanously if user pressed first A then B then C. In this
- /// case only action corresponding
- /// to 'A,B,C' will be triggered.
- /// </param>
- public static void OnSequence(this IKeyboardEvents source, IEnumerable<KeyValuePair<Sequence, Action>> map)
- {
- var actBySeq = map.ToArray();
- var endsWith = new Func<Queue<Combination>, Sequence, bool>((chords, sequence) =>
- {
- var skipCount = chords.Count - sequence.Length;
- return skipCount >= 0 && chords.Skip(skipCount).SequenceEqual(sequence);
- });
-
- var max = actBySeq.Select(p => p.Key).Max(c => c.Length);
- var min = actBySeq.Select(p => p.Key).Min(c => c.Length);
- var buffer = new Queue<Combination>(max);
-
- var wrapMap = actBySeq.SelectMany(p => p.Key).Select(c => new KeyValuePair<Combination, Action>(c, () =>
- {
- buffer.Enqueue(c);
- if (buffer.Count > max) buffer.Dequeue();
- if (buffer.Count < min) return;
- //Invoke action corresponding to the longest matching sequence
- actBySeq
- .Where(pair => endsWith(buffer, pair.Key))
- .OrderBy(pair => pair.Key.Length)
- .Select(pair => pair.Value)
- .LastOrDefault()
- ?.Invoke();
- }));
-
- OnCombination(source, wrapMap, buffer.Clear);
- }
- }
- }
|