A game framework written with osu! in mind.
at master 194 lines 7.5 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.Linq; 5using System; 6using osu.Framework.Graphics; 7using osu.Framework.Graphics.Containers; 8using osu.Framework.Input; 9using osu.Framework.Input.Events; 10using osu.Framework.Input.Handlers; 11using osu.Framework.Input.StateChanges; 12using osu.Framework.Platform; 13using osuTK; 14using osuTK.Input; 15 16namespace osu.Framework.Testing.Input 17{ 18 public class ManualInputManager : PassThroughInputManager 19 { 20 private readonly ManualInputHandler handler; 21 22 protected override Container<Drawable> Content => content; 23 24 private bool showVisualCursorGuide = true; 25 26 /// <summary> 27 /// Whether to show a visible cursor tracking position and clicks. 28 /// Generally should be enabled unless it blocks the test's content. 29 /// </summary> 30 public bool ShowVisualCursorGuide 31 { 32 get => showVisualCursorGuide; 33 set 34 { 35 if (value == showVisualCursorGuide) 36 return; 37 38 showVisualCursorGuide = value; 39 testCursor.State.Value = value ? Visibility.Visible : Visibility.Hidden; 40 } 41 } 42 43 private readonly Container content; 44 45 private readonly TestCursorContainer testCursor; 46 47 private readonly LocalPlatformActionContainer platformActionContainer; 48 49 public override bool UseParentInput 50 { 51 get => base.UseParentInput; 52 set 53 { 54 base.UseParentInput = value; 55 platformActionContainer.ShouldHandle = !value; 56 } 57 } 58 59 public ManualInputManager() 60 { 61 AddHandler(handler = new ManualInputHandler()); 62 63 InternalChildren = new Drawable[] 64 { 65 platformActionContainer = new LocalPlatformActionContainer().WithChild(content = new Container { RelativeSizeAxes = Axes.Both }), 66 testCursor = new TestCursorContainer(), 67 }; 68 69 UseParentInput = true; 70 } 71 72 public void Input(IInput input) 73 { 74 UseParentInput = false; 75 handler.EnqueueInput(input); 76 } 77 78 /// <summary> 79 /// Press a key down. Release with <see cref="ReleaseKey"/>. 80 /// </summary> 81 /// <remarks> 82 /// To press and release a key immediately, use <see cref="Key"/>. 83 /// </remarks> 84 /// <param name="key">The key to press.</param> 85 public void PressKey(Key key) => Input(new KeyboardKeyInput(key, true)); 86 87 /// <summary> 88 /// Release a pressed key. 89 /// </summary> 90 /// <param name="key">The key to release.</param> 91 public void ReleaseKey(Key key) => Input(new KeyboardKeyInput(key, false)); 92 93 /// <summary> 94 /// Press and release the specified key. 95 /// </summary> 96 /// <param name="key">The key to actuate.</param> 97 public void Key(Key key) 98 { 99 PressKey(key); 100 ReleaseKey(key); 101 } 102 103 /// <summary> 104 /// Press and release the keys in the specified <see cref="PlatformAction"/>. 105 /// </summary> 106 /// <param name="action">The platform action to actuate.</param> 107 public void Keys(PlatformAction action) 108 { 109 var binding = Host.PlatformKeyBindings.First(b => (PlatformAction)b.Action == action); 110 111 foreach (var k in binding.KeyCombination.Keys) 112 PressKey((Key)k); 113 114 foreach (var k in binding.KeyCombination.Keys) 115 ReleaseKey((Key)k); 116 } 117 118 public void ScrollBy(Vector2 delta, bool isPrecise = false) => Input(new MouseScrollRelativeInput { Delta = delta, IsPrecise = isPrecise }); 119 public void ScrollHorizontalBy(float delta, bool isPrecise = false) => ScrollBy(new Vector2(delta, 0), isPrecise); 120 public void ScrollVerticalBy(float delta, bool isPrecise = false) => ScrollBy(new Vector2(0, delta), isPrecise); 121 122 public void MoveMouseTo(Drawable drawable, Vector2? offset = null) => MoveMouseTo(drawable.ToScreenSpace(drawable.LayoutRectangle.Centre) + (offset ?? Vector2.Zero)); 123 public void MoveMouseTo(Vector2 position) => Input(new MousePositionAbsoluteInput { Position = position }); 124 125 public void MoveTouchTo(Touch touch) => Input(new TouchInput(touch, CurrentState.Touch.IsActive(touch.Source))); 126 127 public new bool TriggerClick() => 128 throw new InvalidOperationException($"To trigger a click via a {nameof(ManualInputManager)} use {nameof(Click)} instead."); 129 130 /// <summary> 131 /// Press and release the specified button. 132 /// </summary> 133 /// <param name="button">The button to press and release.</param> 134 public void Click(MouseButton button) 135 { 136 PressButton(button); 137 ReleaseButton(button); 138 } 139 140 /// <summary> 141 /// Press a mouse button down. Release with <see cref="ReleaseButton"/>. 142 /// </summary> 143 /// <remarks> 144 /// To press and release a mouse button immediately, use <see cref="Click"/>. 145 /// </remarks> 146 /// <param name="button">The button to press.</param> 147 public void PressButton(MouseButton button) => Input(new MouseButtonInput(button, true)); 148 149 /// <summary> 150 /// Release a pressed mouse button. 151 /// </summary> 152 /// <param name="button">The button to release.</param> 153 public void ReleaseButton(MouseButton button) => Input(new MouseButtonInput(button, false)); 154 155 public void PressJoystickButton(JoystickButton button) => Input(new JoystickButtonInput(button, true)); 156 public void ReleaseJoystickButton(JoystickButton button) => Input(new JoystickButtonInput(button, false)); 157 158 public void BeginTouch(Touch touch) => Input(new TouchInput(touch, true)); 159 public void EndTouch(Touch touch) => Input(new TouchInput(touch, false)); 160 161 public void PressMidiKey(MidiKey key, byte velocity) => Input(new MidiKeyInput(key, velocity, true)); 162 public void ReleaseMidiKey(MidiKey key, byte velocity) => Input(new MidiKeyInput(key, velocity, false)); 163 164 public void PressTabletPenButton(TabletPenButton penButton) => Input(new TabletPenButtonInput(penButton, true)); 165 public void ReleaseTabletPenButton(TabletPenButton penButton) => Input(new TabletPenButtonInput(penButton, false)); 166 167 public void PressTabletAuxiliaryButton(TabletAuxiliaryButton auxiliaryButton) => Input(new TabletAuxiliaryButtonInput(auxiliaryButton, true)); 168 public void ReleaseTabletAuxiliaryButton(TabletAuxiliaryButton auxiliaryButton) => Input(new TabletAuxiliaryButtonInput(auxiliaryButton, false)); 169 170 private class LocalPlatformActionContainer : PlatformActionContainer 171 { 172 public bool ShouldHandle; 173 174 protected override bool Handle(UIEvent e) 175 { 176 if (!ShouldHandle) 177 return false; 178 179 return base.Handle(e); 180 } 181 } 182 183 private class ManualInputHandler : InputHandler 184 { 185 public override bool Initialize(GameHost host) => true; 186 public override bool IsActive => true; 187 188 public void EnqueueInput(IInput input) 189 { 190 PendingInputs.Enqueue(input); 191 } 192 } 193 } 194}