···12 private static readonly object[][] key_combination_display_test_cases =
13 {
14 // test single combination matches.
15- new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.LShift), false, true },
16- new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.RShift), false, true },
17- new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift), false, true },
18- new object[] { new KeyCombination(InputKey.LShift), new KeyCombination(InputKey.RShift), false, false },
19- new object[] { new KeyCombination(InputKey.RShift), new KeyCombination(InputKey.RShift), false, true },
2021- new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.LShift), true, true },
22- new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.RShift), true, true },
23- new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift), true, true },
24- new object[] { new KeyCombination(InputKey.LShift), new KeyCombination(InputKey.RShift), true, false },
25- new object[] { new KeyCombination(InputKey.RShift), new KeyCombination(InputKey.RShift), true, true },
0000002627 // test multiple combination matches.
28- new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift, InputKey.LShift), false, true },
29- new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift, InputKey.RShift), false, true },
30- new object[] { new KeyCombination(InputKey.LShift), new KeyCombination(InputKey.Shift, InputKey.RShift), false, false },
31- new object[] { new KeyCombination(InputKey.RShift), new KeyCombination(InputKey.Shift, InputKey.RShift), false, true },
000003233- new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift, InputKey.LShift), true, true },
34- new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift, InputKey.RShift), true, true },
35- new object[] { new KeyCombination(InputKey.LShift), new KeyCombination(InputKey.Shift, InputKey.RShift), true, false },
36- new object[] { new KeyCombination(InputKey.RShift), new KeyCombination(InputKey.Shift, InputKey.RShift), true, true },
37 };
3839 [TestCaseSource(nameof(key_combination_display_test_cases))]
40- public void TestLeftRightModifierHandling(KeyCombination candidate, KeyCombination pressed, bool exactModifiers, bool shouldContain)
41- => Assert.AreEqual(shouldContain, KeyCombination.ContainsAll(candidate.Keys, pressed.Keys, exactModifiers));
42 }
43}
···12 private static readonly object[][] key_combination_display_test_cases =
13 {
14 // test single combination matches.
15+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.LShift), KeyCombinationMatchingMode.Any, true },
16+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.RShift), KeyCombinationMatchingMode.Any, true },
17+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift), KeyCombinationMatchingMode.Any, true },
18+ new object[] { new KeyCombination(InputKey.LShift), new KeyCombination(InputKey.RShift), KeyCombinationMatchingMode.Any, false },
19+ new object[] { new KeyCombination(InputKey.RShift), new KeyCombination(InputKey.RShift), KeyCombinationMatchingMode.Any, true },
2021+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.LShift), KeyCombinationMatchingMode.Exact, true },
22+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.RShift), KeyCombinationMatchingMode.Exact, true },
23+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift), KeyCombinationMatchingMode.Exact, true },
24+ new object[] { new KeyCombination(InputKey.LShift), new KeyCombination(InputKey.RShift), KeyCombinationMatchingMode.Exact, false },
25+ new object[] { new KeyCombination(InputKey.RShift), new KeyCombination(InputKey.RShift), KeyCombinationMatchingMode.Exact, true },
26+27+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.LShift), KeyCombinationMatchingMode.Modifiers, true },
28+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.RShift), KeyCombinationMatchingMode.Modifiers, true },
29+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift), KeyCombinationMatchingMode.Modifiers, true },
30+ new object[] { new KeyCombination(InputKey.LShift), new KeyCombination(InputKey.RShift), KeyCombinationMatchingMode.Modifiers, false },
31+ new object[] { new KeyCombination(InputKey.RShift), new KeyCombination(InputKey.RShift), KeyCombinationMatchingMode.Modifiers, true },
3233 // test multiple combination matches.
34+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift, InputKey.LShift), KeyCombinationMatchingMode.Any, true },
35+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift, InputKey.RShift), KeyCombinationMatchingMode.Any, true },
36+ new object[] { new KeyCombination(InputKey.LShift), new KeyCombination(InputKey.Shift, InputKey.RShift), KeyCombinationMatchingMode.Any, false },
37+ new object[] { new KeyCombination(InputKey.RShift), new KeyCombination(InputKey.Shift, InputKey.RShift), KeyCombinationMatchingMode.Any, true },
38+39+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift, InputKey.LShift), KeyCombinationMatchingMode.Exact, true },
40+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift, InputKey.RShift), KeyCombinationMatchingMode.Exact, true },
41+ new object[] { new KeyCombination(InputKey.LShift), new KeyCombination(InputKey.Shift, InputKey.RShift), KeyCombinationMatchingMode.Exact, false },
42+ new object[] { new KeyCombination(InputKey.RShift), new KeyCombination(InputKey.Shift, InputKey.RShift), KeyCombinationMatchingMode.Exact, true },
4344+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift, InputKey.LShift), KeyCombinationMatchingMode.Modifiers, true },
45+ new object[] { new KeyCombination(InputKey.Shift), new KeyCombination(InputKey.Shift, InputKey.RShift), KeyCombinationMatchingMode.Modifiers, true },
46+ new object[] { new KeyCombination(InputKey.LShift), new KeyCombination(InputKey.Shift, InputKey.RShift), KeyCombinationMatchingMode.Modifiers, false },
47+ new object[] { new KeyCombination(InputKey.RShift), new KeyCombination(InputKey.Shift, InputKey.RShift), KeyCombinationMatchingMode.Modifiers, true },
48 };
4950 [TestCaseSource(nameof(key_combination_display_test_cases))]
51+ public void TestLeftRightModifierHandling(KeyCombination candidate, KeyCombination pressed, KeyCombinationMatchingMode matchingMode, bool shouldContain)
52+ => Assert.AreEqual(shouldContain, KeyCombination.ContainsAll(candidate.Keys, pressed.Keys, matchingMode));
53 }
54}
+21-23
osu.Framework/Input/Bindings/KeyCombination.cs
···78 if (Keys == pressedKeys.Keys) // Fast test for reference equality of underlying array
79 return true;
8081- switch (matchingMode)
82- {
83- case KeyCombinationMatchingMode.Any:
84- return ContainsAll(pressedKeys.Keys, Keys, false);
85-86- case KeyCombinationMatchingMode.Exact:
87- // Keys are always ordered
88- return pressedKeys.Keys.SequenceEqual(Keys);
89-90- case KeyCombinationMatchingMode.Modifiers:
91- return ContainsAll(pressedKeys.Keys, Keys, true);
92-93- default:
94- return false;
95- }
96 }
9798 /// <summary>
···100 /// </summary>
101 /// <param name="pressedKey">The keys which have been pressed by a user.</param>
102 /// <param name="candidateKey">The candidate key binding to match against.</param>
103- /// <param name="exactModifiers">Whether exact matching should be used (ie. no excess modifier keys can be pressed).</param>
104 /// <returns>Whether this is a match.</returns>
105 [MethodImpl(MethodImplOptions.AggressiveInlining)]
106- internal static bool ContainsAll(ImmutableArray<InputKey> pressedKey, ImmutableArray<InputKey> candidateKey, bool exactModifiers)
107 {
108 // can be local function once attribute on local functions are implemented
109 // optimized to avoid allocation
···114 return false;
115 }
116117- if (exactModifiers)
118 {
119- foreach (var key in pressedKey)
120- {
121- if (IsModifierKey(key) && !ContainsKey(candidateKey, key))
122- return false;
123- }
000000000000124 }
125126 return true;
···78 if (Keys == pressedKeys.Keys) // Fast test for reference equality of underlying array
79 return true;
8081+ return ContainsAll(pressedKeys.Keys, Keys, matchingMode);
0000000000000082 }
8384 /// <summary>
···86 /// </summary>
87 /// <param name="pressedKey">The keys which have been pressed by a user.</param>
88 /// <param name="candidateKey">The candidate key binding to match against.</param>
89+ /// <param name="matchingMode">The matching mode to be used when checking.</param>
90 /// <returns>Whether this is a match.</returns>
91 [MethodImpl(MethodImplOptions.AggressiveInlining)]
92+ internal static bool ContainsAll(ImmutableArray<InputKey> pressedKey, ImmutableArray<InputKey> candidateKey, KeyCombinationMatchingMode matchingMode)
93 {
94 // can be local function once attribute on local functions are implemented
95 // optimized to avoid allocation
···100 return false;
101 }
102103+ switch (matchingMode)
104 {
105+ case KeyCombinationMatchingMode.Exact:
106+ foreach (var key in pressedKey)
107+ {
108+ if (!ContainsKey(candidateKey, key))
109+ return false;
110+ }
111+112+ break;
113+114+ case KeyCombinationMatchingMode.Modifiers:
115+ foreach (var key in pressedKey)
116+ {
117+ if (IsModifierKey(key) && !ContainsKey(candidateKey, key))
118+ return false;
119+ }
120+121+ break;
122 }
123124 return true;