A game framework written with osu! in mind.
at master 628 lines 25 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using System.Collections.Generic; 6using System.Diagnostics; 7using System.Linq; 8using NUnit.Framework; 9using osu.Framework.Graphics; 10using osu.Framework.Graphics.Containers; 11using osu.Framework.Graphics.Shapes; 12using osu.Framework.Graphics.Sprites; 13using osu.Framework.Graphics.UserInterface; 14using osu.Framework.Input.Bindings; 15using osu.Framework.Input.Events; 16using osu.Framework.Testing; 17using osuTK; 18using osuTK.Graphics; 19using osuTK.Input; 20 21namespace osu.Framework.Tests.Visual.Input 22{ 23 public class TestSceneKeyBindingsGrid : ManualInputManagerTestScene 24 { 25 private readonly KeyBindingTester none, noneExact, noneModifiers, unique, all; 26 27 public TestSceneKeyBindingsGrid() 28 { 29 Child = new GridContainer 30 { 31 RelativeSizeAxes = Axes.Both, 32 Content = new[] 33 { 34 new Drawable[] 35 { 36 none = new KeyBindingTester(SimultaneousBindingMode.None, KeyCombinationMatchingMode.Any), 37 noneExact = new KeyBindingTester(SimultaneousBindingMode.None, KeyCombinationMatchingMode.Exact), 38 noneModifiers = new KeyBindingTester(SimultaneousBindingMode.None, KeyCombinationMatchingMode.Modifiers) 39 }, 40 new Drawable[] 41 { 42 unique = new KeyBindingTester(SimultaneousBindingMode.Unique, KeyCombinationMatchingMode.Any), 43 all = new KeyBindingTester(SimultaneousBindingMode.All, KeyCombinationMatchingMode.Any) 44 }, 45 } 46 }; 47 } 48 49 private readonly List<Key> pressedKeys = new List<Key>(); 50 private readonly List<MouseButton> pressedMouseButtons = new List<MouseButton>(); 51 private readonly Dictionary<TestButton, EventCounts> lastEventCounts = new Dictionary<TestButton, EventCounts>(); 52 53 private void toggleKey(Key key) 54 { 55 if (!pressedKeys.Contains(key)) 56 { 57 pressedKeys.Add(key); 58 AddStep($"press {key}", () => InputManager.PressKey(key)); 59 } 60 else 61 { 62 pressedKeys.Remove(key); 63 AddStep($"release {key}", () => InputManager.ReleaseKey(key)); 64 } 65 } 66 67 private void toggleMouseButton(MouseButton button) 68 { 69 if (!pressedMouseButtons.Contains(button)) 70 { 71 pressedMouseButtons.Add(button); 72 AddStep($"press {button}", () => InputManager.PressButton(button)); 73 } 74 else 75 { 76 pressedMouseButtons.Remove(button); 77 AddStep($"release {button}", () => InputManager.ReleaseButton(button)); 78 } 79 } 80 81 private void scrollMouseWheel(int dx, int dy) 82 { 83 if (dx != 0) AddStep($"scroll wheel horizontally {dx}", () => InputManager.ScrollHorizontalBy(dx)); 84 if (dy != 0) AddStep($"scroll wheel {dy}", () => InputManager.ScrollVerticalBy(dy)); 85 } 86 87 private void check(TestAction action, params CheckConditions[] entries) 88 { 89 AddAssert($"check {action}", () => 90 { 91 Assert.Multiple(() => 92 { 93 foreach (var entry in entries) 94 { 95 var scrollEntry = entry as ScrollCheckConditions; 96 var testButton = entry.Tester[action]; 97 98 if (!lastEventCounts.TryGetValue(testButton, out var count)) 99 lastEventCounts[testButton] = count = new EventCounts(); 100 101 count.OnPressedCount += entry.OnPressedDelta; 102 count.OnReleasedCount += entry.OnReleasedDelta; 103 count.OnScrollCount += scrollEntry?.OnScrollCount ?? 0; 104 105 Assert.AreEqual(count.OnPressedCount, testButton.OnPressedCount, $"{testButton.Concurrency} {testButton.Action} OnPressedCount"); 106 Assert.AreEqual(count.OnReleasedCount, testButton.OnReleasedCount, $"{testButton.Concurrency} {testButton.Action} OnReleasedCount"); 107 108 if (testButton is ScrollTestButton scrollTestButton && scrollEntry != null) 109 { 110 Assert.AreEqual(count.OnScrollCount, scrollTestButton.OnScrollCount, $"{testButton.Concurrency} {testButton.Action} OnScrollCount"); 111 Assert.AreEqual(scrollEntry.LastScrollAmount, scrollTestButton.LastScrollAmount, $"{testButton.Concurrency} {testButton.Action} LastScrollAmount"); 112 } 113 } 114 }); 115 return true; 116 }); 117 } 118 119 private void checkPressed(TestAction action, int noneDelta, int noneExactDelta, int noneModifiersDelta, int uniqueDelta, int allDelta) 120 { 121 check(action, 122 new CheckConditions(none, noneDelta, 0), 123 new CheckConditions(noneExact, noneExactDelta, 0), 124 new CheckConditions(noneModifiers, noneModifiersDelta, 0), 125 new CheckConditions(unique, uniqueDelta, 0), 126 new CheckConditions(all, allDelta, 0)); 127 } 128 129 private void checkReleased(TestAction action, int noneDelta, int noneExactDelta, int noneModifiersDelta, int uniqueDelta, int allDelta) 130 { 131 check(action, 132 new CheckConditions(none, 0, noneDelta), 133 new CheckConditions(noneExact, 0, noneExactDelta), 134 new CheckConditions(noneModifiers, 0, noneModifiersDelta), 135 new CheckConditions(unique, 0, uniqueDelta), 136 new CheckConditions(all, 0, allDelta)); 137 } 138 139 private void wrapTest(Action inner) 140 { 141 AddStep("init", () => 142 { 143 foreach (var mode in new[] { none, noneExact, noneModifiers, unique, all }) 144 { 145 foreach (var action in Enum.GetValues(typeof(TestAction)).Cast<TestAction>()) 146 { 147 mode[action].Reset(); 148 } 149 } 150 151 lastEventCounts.Clear(); 152 }); 153 pressedKeys.Clear(); 154 pressedMouseButtons.Clear(); 155 inner(); 156 foreach (var key in pressedKeys.ToArray()) 157 toggleKey(key); 158 foreach (var button in pressedMouseButtons.ToArray()) 159 toggleMouseButton(button); 160 161 foreach (var mode in new[] { none, noneExact, noneModifiers, unique, all }) 162 { 163 foreach (var action in Enum.GetValues(typeof(TestAction)).Cast<TestAction>()) 164 { 165 var testButton = mode[action]; 166 Trace.Assert(testButton.OnPressedCount == testButton.OnReleasedCount); 167 if (testButton is ScrollTestButton scrollTestButton) 168 Trace.Assert(scrollTestButton.OnScrollCount == testButton.OnPressedCount); 169 } 170 } 171 } 172 173 [Test] 174 public void SimultaneousBindingModes() 175 { 176 wrapTest(() => 177 { 178 toggleKey(Key.A); 179 checkPressed(TestAction.A, 1, 1, 1, 1, 1); 180 toggleKey(Key.S); 181 checkReleased(TestAction.A, 1, 1, 1, 0, 0); 182 checkPressed(TestAction.S, 1, 0, 1, 1, 1); 183 toggleKey(Key.A); 184 checkReleased(TestAction.A, 0, 0, 0, 1, 1); 185 checkPressed(TestAction.S, 0, 0, 0, 0, 0); 186 toggleKey(Key.S); 187 checkReleased(TestAction.S, 1, 0, 1, 1, 1); 188 189 toggleKey(Key.D); 190 checkPressed(TestAction.D_or_F, 1, 1, 1, 1, 1); 191 toggleKey(Key.F); 192 check(TestAction.D_or_F, new CheckConditions(none, 1, 1), new CheckConditions(noneExact, 0, 1), new CheckConditions(noneModifiers, 1, 1), new CheckConditions(unique, 0, 0), new CheckConditions(all, 1, 0)); 193 toggleKey(Key.F); 194 checkReleased(TestAction.D_or_F, 0, 0, 0, 0, 1); 195 toggleKey(Key.D); 196 checkReleased(TestAction.D_or_F, 1, 0, 1, 1, 1); 197 198 toggleKey(Key.ShiftLeft); 199 toggleKey(Key.AltLeft); 200 checkPressed(TestAction.Alt_and_LShift, 1, 1, 1, 1, 1); 201 toggleKey(Key.A); 202 checkPressed(TestAction.LShift_A, 1, 0, 0, 1, 1); 203 toggleKey(Key.AltLeft); 204 toggleKey(Key.ShiftLeft); 205 }); 206 } 207 208 [Test] 209 public void PerSideModifierKeys() 210 { 211 wrapTest(() => 212 { 213 toggleKey(Key.ShiftLeft); 214 checkPressed(TestAction.AnyShift, 1, 1, 1, 1, 1); 215 checkPressed(TestAction.LShift, 0, 0, 0, 1, 1); 216 checkPressed(TestAction.RShift, 0, 0, 0, 0, 0); 217 218 toggleKey(Key.A); 219 checkPressed(TestAction.AnyShift_A, 0, 0, 0, 1, 1); 220 checkPressed(TestAction.LShift_A, 1, 1, 1, 1, 1); 221 checkReleased(TestAction.AnyShift, 1, 1, 1, 0, 0); 222 223 toggleKey(Key.ShiftRight); 224 checkReleased(TestAction.LShift_A, 1, 0, 0, 0, 0); 225 checkPressed(TestAction.RShift, 1, 0, 0, 1, 1); 226 checkPressed(TestAction.AnyShift, 0, 0, 0, 0, 0); 227 228 toggleKey(Key.ShiftLeft); 229 checkReleased(TestAction.LShift, 0, 0, 0, 1, 1); 230 checkReleased(TestAction.LShift_A, 0, 1, 1, 1, 1); 231 checkReleased(TestAction.AnyShift, 0, 0, 0, 0, 0); 232 233 toggleKey(Key.ShiftRight); 234 checkReleased(TestAction.AnyShift, 0, 0, 0, 1, 1); 235 }); 236 } 237 238 [Test] 239 public void BothSideModifierKeys() 240 { 241 wrapTest(() => 242 { 243 toggleKey(Key.AltLeft); 244 checkPressed(TestAction.Alt, 1, 1, 1, 1, 1); 245 toggleKey(Key.A); 246 checkReleased(TestAction.Alt, 1, 1, 1, 0, 0); 247 checkPressed(TestAction.Alt_A, 1, 1, 1, 1, 1); 248 toggleKey(Key.AltRight); 249 checkPressed(TestAction.Alt, 0, 0, 0, 0, 0); 250 checkReleased(TestAction.Alt_A, 0, 0, 0, 0, 0); 251 toggleKey(Key.AltLeft); 252 checkReleased(TestAction.Alt, 0, 0, 0, 0, 0); 253 checkReleased(TestAction.Alt_A, 0, 0, 0, 0, 0); 254 toggleKey(Key.AltRight); 255 checkReleased(TestAction.Alt, 0, 0, 0, 1, 1); 256 checkReleased(TestAction.Alt_A, 1, 1, 1, 1, 1); 257 toggleKey(Key.A); 258 259 toggleKey(Key.ControlLeft); 260 toggleKey(Key.AltLeft); 261 checkPressed(TestAction.Ctrl_and_Alt, 1, 1, 1, 1, 1); 262 }); 263 } 264 265 [Test] 266 public void MouseScrollAndButtons() 267 { 268 wrapTest(() => 269 { 270 var allPressAndReleased = new[] 271 { 272 new CheckConditions(none, 1, 1), 273 new CheckConditions(noneExact, 1, 1), 274 new CheckConditions(noneModifiers, 1, 1), 275 new CheckConditions(unique, 1, 1), 276 new CheckConditions(all, 1, 1) 277 }; 278 279 scrollMouseWheel(0, 1); 280 check(TestAction.WheelUp, allPressAndReleased); 281 scrollMouseWheel(0, -1); 282 check(TestAction.WheelDown, allPressAndReleased); 283 284 scrollMouseWheel(-1, 0); 285 check(TestAction.WheelLeft, allPressAndReleased); 286 scrollMouseWheel(1, 0); 287 check(TestAction.WheelRight, allPressAndReleased); 288 289 toggleKey(Key.ControlLeft); 290 scrollMouseWheel(0, 1); 291 toggleKey(Key.ControlLeft); 292 check(TestAction.Ctrl_and_WheelUp, allPressAndReleased); 293 toggleMouseButton(MouseButton.Left); 294 toggleMouseButton(MouseButton.Left); 295 check(TestAction.LeftMouse, allPressAndReleased); 296 toggleMouseButton(MouseButton.Right); 297 toggleMouseButton(MouseButton.Right); 298 check(TestAction.RightMouse, allPressAndReleased); 299 }); 300 } 301 302 [Test] 303 public void Scroll() 304 { 305 wrapTest(() => 306 { 307 CheckConditions[] allPressAndReleased(float amount) => new CheckConditions[] 308 { 309 new ScrollCheckConditions(none, 1, 1, 1, amount), 310 new ScrollCheckConditions(noneExact, 1, 1, 1, amount), 311 new ScrollCheckConditions(noneModifiers, 1, 1, 1, amount), 312 new ScrollCheckConditions(unique, 1, 1, 1, amount), 313 new ScrollCheckConditions(all, 1, 1, 1, amount) 314 }; 315 316 scrollMouseWheel(0, 2); 317 check(TestAction.WheelUp, allPressAndReleased(2)); 318 scrollMouseWheel(0, -3); 319 check(TestAction.WheelDown, allPressAndReleased(3)); 320 toggleKey(Key.ControlLeft); 321 scrollMouseWheel(0, 4); 322 toggleKey(Key.ControlLeft); 323 check(TestAction.Ctrl_and_WheelUp, allPressAndReleased(4)); 324 }); 325 } 326 327 private class EventCounts 328 { 329 public int OnPressedCount; 330 public int OnReleasedCount; 331 public int OnScrollCount; 332 } 333 334 private class CheckConditions 335 { 336 public readonly KeyBindingTester Tester; 337 public readonly int OnPressedDelta; 338 public readonly int OnReleasedDelta; 339 340 public CheckConditions(KeyBindingTester tester, int onPressedDelta, int onReleasedDelta) 341 { 342 Tester = tester; 343 OnPressedDelta = onPressedDelta; 344 OnReleasedDelta = onReleasedDelta; 345 } 346 } 347 348 private class ScrollCheckConditions : CheckConditions 349 { 350 public readonly int OnScrollCount; 351 public readonly float LastScrollAmount; 352 353 public ScrollCheckConditions(KeyBindingTester tester, int onPressedDelta, int onReleasedDelta, int onScrollCount, float lastScrollAmount) 354 : base(tester, onPressedDelta, onReleasedDelta) 355 { 356 OnScrollCount = onScrollCount; 357 LastScrollAmount = lastScrollAmount; 358 } 359 } 360 361 private enum TestAction 362 { 363 A, 364 S, 365 D_or_F, 366 Ctrl_A, 367 Ctrl_S, 368 Ctrl_D_or_F, 369 Alt_A, 370 Alt_S, 371 Alt_D_or_F, 372 LShift_A, 373 LShift_S, 374 LShift_D_or_F, 375 RShift_A, 376 RShift_S, 377 RShift_D_or_F, 378 Ctrl_Shift_A, 379 Ctrl_Shift_S, 380 Ctrl_Shift_D_or_F, 381 Ctrl, 382 RShift, 383 LShift, 384 Alt, 385 Alt_and_LShift, 386 Ctrl_and_Alt, 387 Ctrl_or_Shift, 388 LeftMouse, 389 RightMouse, 390 WheelUp, 391 WheelDown, 392 WheelLeft, 393 WheelRight, 394 Ctrl_and_WheelUp, 395 AnyShift_A, 396 AnyShift 397 } 398 399 private class TestInputManager : KeyBindingContainer<TestAction> 400 { 401 public TestInputManager(SimultaneousBindingMode concurrencyMode = SimultaneousBindingMode.None, KeyCombinationMatchingMode matchingMode = KeyCombinationMatchingMode.Any) 402 : base(concurrencyMode, matchingMode) 403 { 404 } 405 406 public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[] 407 { 408 new KeyBinding(InputKey.A, TestAction.A), 409 new KeyBinding(InputKey.S, TestAction.S), 410 new KeyBinding(InputKey.D, TestAction.D_or_F), 411 new KeyBinding(InputKey.F, TestAction.D_or_F), 412 413 new KeyBinding(new[] { InputKey.Control, InputKey.A }, TestAction.Ctrl_A), 414 new KeyBinding(new[] { InputKey.Control, InputKey.S }, TestAction.Ctrl_S), 415 new KeyBinding(new[] { InputKey.Control, InputKey.D }, TestAction.Ctrl_D_or_F), 416 new KeyBinding(new[] { InputKey.Control, InputKey.F }, TestAction.Ctrl_D_or_F), 417 418 new KeyBinding(new[] { InputKey.LShift, InputKey.A }, TestAction.LShift_A), 419 new KeyBinding(new[] { InputKey.LShift, InputKey.S }, TestAction.LShift_S), 420 new KeyBinding(new[] { InputKey.LShift, InputKey.D }, TestAction.LShift_D_or_F), 421 new KeyBinding(new[] { InputKey.LShift, InputKey.F }, TestAction.LShift_D_or_F), 422 423 new KeyBinding(new[] { InputKey.RShift, InputKey.A }, TestAction.RShift_A), 424 new KeyBinding(new[] { InputKey.RShift, InputKey.S }, TestAction.RShift_S), 425 new KeyBinding(new[] { InputKey.RShift, InputKey.D }, TestAction.RShift_D_or_F), 426 new KeyBinding(new[] { InputKey.RShift, InputKey.F }, TestAction.RShift_D_or_F), 427 428 new KeyBinding(new[] { InputKey.Shift, InputKey.A }, TestAction.AnyShift_A), 429 430 new KeyBinding(new[] { InputKey.Alt, InputKey.A }, TestAction.Alt_A), 431 new KeyBinding(new[] { InputKey.Alt, InputKey.S }, TestAction.Alt_S), 432 new KeyBinding(new[] { InputKey.Alt, InputKey.D }, TestAction.Alt_D_or_F), 433 new KeyBinding(new[] { InputKey.Alt, InputKey.F }, TestAction.Alt_D_or_F), 434 435 new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.A }, TestAction.Ctrl_Shift_A), 436 new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.S }, TestAction.Ctrl_Shift_S), 437 new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.D }, TestAction.Ctrl_Shift_D_or_F), 438 new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.F }, TestAction.Ctrl_Shift_D_or_F), 439 440 new KeyBinding(new[] { InputKey.Control }, TestAction.Ctrl), 441 new KeyBinding(new[] { InputKey.Shift }, TestAction.AnyShift), 442 new KeyBinding(new[] { InputKey.LShift }, TestAction.LShift), 443 new KeyBinding(new[] { InputKey.RShift }, TestAction.RShift), 444 new KeyBinding(new[] { InputKey.Alt }, TestAction.Alt), 445 new KeyBinding(new[] { InputKey.Alt, InputKey.LShift }, TestAction.Alt_and_LShift), 446 new KeyBinding(new[] { InputKey.Control, InputKey.Alt }, TestAction.Ctrl_and_Alt), 447 new KeyBinding(new[] { InputKey.Control }, TestAction.Ctrl_or_Shift), 448 new KeyBinding(new[] { InputKey.Shift }, TestAction.Ctrl_or_Shift), 449 450 new KeyBinding(new[] { InputKey.MouseLeft }, TestAction.LeftMouse), 451 new KeyBinding(new[] { InputKey.MouseRight }, TestAction.RightMouse), 452 453 new KeyBinding(new[] { InputKey.MouseWheelUp }, TestAction.WheelUp), 454 new KeyBinding(new[] { InputKey.MouseWheelDown }, TestAction.WheelDown), 455 new KeyBinding(new[] { InputKey.MouseWheelLeft }, TestAction.WheelLeft), 456 new KeyBinding(new[] { InputKey.MouseWheelRight }, TestAction.WheelRight), 457 new KeyBinding(new[] { InputKey.Control, InputKey.MouseWheelUp }, TestAction.Ctrl_and_WheelUp), 458 }; 459 460 protected override bool Handle(UIEvent e) 461 { 462 base.Handle(e); 463 return false; 464 } 465 466 public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; 467 } 468 469 private class ScrollTestButton : TestButton, IScrollBindingHandler<TestAction> 470 { 471 public int OnScrollCount { get; protected set; } 472 public float LastScrollAmount { get; protected set; } 473 474 public ScrollTestButton(TestAction action, SimultaneousBindingMode concurrency) 475 : base(action, concurrency) 476 { 477 SpriteText.Font = SpriteText.Font.With(size: SpriteText.Font.Size * .9f); 478 } 479 480 protected override void Update() 481 { 482 base.Update(); 483 Text += $", {OnScrollCount}, {LastScrollAmount}"; 484 } 485 486 public bool OnScroll(TestAction action, float amount, bool isPrecise) 487 { 488 if (Action == action) 489 { 490 ++OnScrollCount; 491 LastScrollAmount = amount; 492 } 493 494 return false; 495 } 496 497 public override void Reset() 498 { 499 base.Reset(); 500 OnScrollCount = 0; 501 LastScrollAmount = 0; 502 } 503 } 504 505 private class TestButton : BasicButton, IKeyBindingHandler<TestAction> 506 { 507 public new readonly TestAction Action; 508 public readonly SimultaneousBindingMode Concurrency; 509 public int OnPressedCount { get; protected set; } 510 public int OnReleasedCount { get; protected set; } 511 512 private readonly Box highlight; 513 private readonly string actionText; 514 515 public TestButton(TestAction action, SimultaneousBindingMode concurrency) 516 { 517 Action = action; 518 Concurrency = concurrency; 519 520 BackgroundColour = Color4.SkyBlue; 521 SpriteText.Font = SpriteText.Font.With(size: SpriteText.Font.Size * .8f); 522 actionText = action.ToString().Replace('_', ' '); 523 524 RelativeSizeAxes = Axes.X; 525 Height = 30; 526 Width = 0.3f; 527 Padding = new MarginPadding(2); 528 529 Background.Alpha = alphaTarget; 530 531 Add(highlight = new Box 532 { 533 RelativeSizeAxes = Axes.Both, 534 Alpha = 0, 535 }); 536 } 537 538 protected override void Update() 539 { 540 Text = $"{actionText}: {OnPressedCount}, {OnReleasedCount}"; 541 base.Update(); 542 } 543 544 private float alphaTarget = 0.5f; 545 546 public bool OnPressed(TestAction action) 547 { 548 if (Action == action) 549 { 550 if (Concurrency != SimultaneousBindingMode.All) 551 Trace.Assert(OnPressedCount == OnReleasedCount); 552 ++OnPressedCount; 553 554 alphaTarget += 0.2f; 555 Background.Alpha = alphaTarget; 556 557 highlight.ClearTransforms(); 558 highlight.Alpha = 1f; 559 highlight.FadeOut(200); 560 561 return true; 562 } 563 564 return false; 565 } 566 567 public void OnReleased(TestAction action) 568 { 569 if (Action == action) 570 { 571 ++OnReleasedCount; 572 573 if (Concurrency != SimultaneousBindingMode.All) 574 Trace.Assert(OnPressedCount == OnReleasedCount); 575 else 576 Trace.Assert(OnReleasedCount <= OnPressedCount); 577 578 alphaTarget -= 0.2f; 579 Background.Alpha = alphaTarget; 580 } 581 } 582 583 public virtual void Reset() 584 { 585 OnPressedCount = 0; 586 OnReleasedCount = 0; 587 } 588 } 589 590 private class KeyBindingTester : FillFlowContainer 591 { 592 private readonly TestButton[] testButtons; 593 594 public KeyBindingTester(SimultaneousBindingMode concurrency, KeyCombinationMatchingMode matchingMode) 595 { 596 RelativeSizeAxes = Axes.Both; 597 Direction = FillDirection.Vertical; 598 599 testButtons = Enum.GetValues(typeof(TestAction)).Cast<TestAction>().Select(t => 600 { 601 if (t.ToString().Contains("Wheel")) 602 return new ScrollTestButton(t, concurrency); 603 604 return new TestButton(t, concurrency); 605 }).ToArray(); 606 607 Children = new Drawable[] 608 { 609 new SpriteText 610 { 611 Text = $"{concurrency} / {matchingMode}" 612 }, 613 new TestInputManager(concurrency, matchingMode) 614 { 615 RelativeSizeAxes = Axes.Both, 616 Child = new FillFlowContainer 617 { 618 RelativeSizeAxes = Axes.Both, 619 Children = testButtons 620 } 621 }, 622 }; 623 } 624 625 public TestButton this[TestAction action] => testButtons.First(x => x.Action == action); 626 } 627 } 628}