A game framework written with osu! in mind.
at master 349 lines 11 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 NUnit.Framework; 6using osu.Framework.Extensions.Color4Extensions; 7using osu.Framework.Graphics; 8using osu.Framework.Graphics.Containers; 9using osu.Framework.Graphics.Shapes; 10using osu.Framework.Graphics.Sprites; 11using osu.Framework.Input; 12using osu.Framework.Input.Events; 13using osu.Framework.Testing; 14using osuTK; 15using osuTK.Graphics; 16using osuTK.Input; 17 18namespace osu.Framework.Tests.Visual.Drawables 19{ 20 public class TestSceneFocus : ManualInputManagerTestScene 21 { 22 private FocusOverlay overlay; 23 private RequestingFocusBox requestingFocus; 24 25 private FocusBox focusTopLeft; 26 private FocusBox focusBottomLeft; 27 private FocusBox focusBottomRight; 28 29 public TestSceneFocus() 30 { 31 RelativeSizeAxes = Axes.Both; 32 } 33 34 [SetUp] 35 public new void SetUp() => Schedule(() => 36 { 37 Children = new Drawable[] 38 { 39 focusTopLeft = new FocusBox 40 { 41 Anchor = Anchor.TopLeft, 42 Origin = Anchor.TopLeft, 43 }, 44 requestingFocus = new RequestingFocusBox 45 { 46 Anchor = Anchor.TopRight, 47 Origin = Anchor.TopRight, 48 }, 49 focusBottomLeft = new FocusBox 50 { 51 Anchor = Anchor.BottomLeft, 52 Origin = Anchor.BottomLeft, 53 }, 54 focusBottomRight = new FocusBox 55 { 56 Anchor = Anchor.BottomRight, 57 Origin = Anchor.BottomRight, 58 }, 59 overlay = new FocusOverlay 60 { 61 Anchor = Anchor.Centre, 62 Origin = Anchor.Centre, 63 } 64 }; 65 }); 66 67 [Test] 68 public void FocusedOverlayTakesFocusOnShow() 69 { 70 AddAssert("overlay not visible", () => overlay.State.Value == Visibility.Hidden); 71 checkNotFocused(() => overlay); 72 73 AddStep("show overlay", () => overlay.Show()); 74 checkFocused(() => overlay); 75 76 AddStep("hide overlay", () => overlay.Hide()); 77 checkNotFocused(() => overlay); 78 } 79 80 [Test] 81 public void FocusedOverlayLosesFocusOnClickAway() 82 { 83 AddAssert("overlay not visible", () => overlay.State.Value == Visibility.Hidden); 84 checkNotFocused(() => overlay); 85 86 AddStep("show overlay", () => overlay.Show()); 87 checkFocused(() => overlay); 88 89 AddStep("click away", () => 90 { 91 InputManager.MoveMouseTo(Vector2.One); 92 InputManager.Click(MouseButton.Left); 93 }); 94 95 checkNotFocused(() => overlay); 96 checkFocused(() => requestingFocus); 97 } 98 99 [Test] 100 public void RequestsFocusKeepsFocusOnClickAway() 101 { 102 checkFocused(() => requestingFocus); 103 104 AddStep("click away", () => 105 { 106 InputManager.MoveMouseTo(Vector2.One); 107 InputManager.Click(MouseButton.Left); 108 }); 109 110 checkFocused(() => requestingFocus); 111 } 112 113 [Test] 114 public void RequestsFocusLosesFocusOnClickingFocused() 115 { 116 checkFocused(() => requestingFocus); 117 118 AddStep("click top left", () => 119 { 120 InputManager.MoveMouseTo(focusTopLeft); 121 InputManager.Click(MouseButton.Left); 122 }); 123 124 checkFocused(() => focusTopLeft); 125 126 AddStep("click bottom right", () => 127 { 128 InputManager.MoveMouseTo(focusBottomRight); 129 InputManager.Click(MouseButton.Left); 130 }); 131 132 checkFocused(() => focusBottomRight); 133 } 134 135 [Test] 136 public void ShowOverlayInteractions() 137 { 138 AddStep("click bottom left", () => 139 { 140 InputManager.MoveMouseTo(focusBottomLeft); 141 InputManager.Click(MouseButton.Left); 142 }); 143 144 checkFocused(() => focusBottomLeft); 145 146 AddStep("show overlay", () => overlay.Show()); 147 148 checkFocused(() => overlay); 149 checkNotFocused(() => focusBottomLeft); 150 151 // click is blocked by overlay so doesn't select bottom left first click 152 AddStep("click", () => InputManager.Click(MouseButton.Left)); 153 checkFocused(() => requestingFocus); 154 155 // second click selects bottom left 156 AddStep("click", () => InputManager.Click(MouseButton.Left)); 157 checkFocused(() => focusBottomLeft); 158 159 // further click has no effect 160 AddStep("click", () => InputManager.Click(MouseButton.Left)); 161 checkFocused(() => focusBottomLeft); 162 } 163 164 [Test] 165 public void InputPropagation() 166 { 167 AddStep("Focus bottom left", () => 168 { 169 InputManager.MoveMouseTo(focusBottomLeft); 170 InputManager.Click(MouseButton.Left); 171 }); 172 AddStep("Press a key (blocking)", () => 173 { 174 InputManager.PressKey(Key.A); 175 InputManager.ReleaseKey(Key.A); 176 }); 177 AddAssert("Received the key", () => 178 focusBottomLeft.KeyDownCount == 1 && focusBottomLeft.KeyUpCount == 1 && 179 focusBottomRight.KeyDownCount == 0 && focusBottomRight.KeyUpCount == 0); 180 AddStep("Press a joystick (non blocking)", () => 181 { 182 InputManager.PressJoystickButton(JoystickButton.Button1); 183 InputManager.ReleaseJoystickButton(JoystickButton.Button1); 184 }); 185 AddAssert("Received the joystick button", () => 186 focusBottomLeft.JoystickPressCount == 1 && focusBottomLeft.JoystickReleaseCount == 1 && 187 focusBottomRight.JoystickPressCount == 1 && focusBottomRight.JoystickReleaseCount == 1); 188 } 189 190 private void checkFocused(Func<Drawable> d) => AddAssert("check focus", () => d().HasFocus); 191 private void checkNotFocused(Func<Drawable> d) => AddAssert("check not focus", () => !d().HasFocus); 192 193 private class FocusOverlay : FocusedOverlayContainer 194 { 195 private readonly Box box; 196 private readonly SpriteText stateText; 197 198 public FocusOverlay() 199 { 200 RelativeSizeAxes = Axes.Both; 201 202 Children = new Drawable[] 203 { 204 new Box 205 { 206 RelativeSizeAxes = Axes.Both, 207 Colour = Color4.Gray.Opacity(0.5f), 208 }, 209 box = new Box 210 { 211 RelativeSizeAxes = Axes.Both, 212 Size = new Vector2(0.4f), 213 Anchor = Anchor.Centre, 214 Origin = Anchor.Centre, 215 Colour = Color4.Blue, 216 }, 217 new SpriteText 218 { 219 Text = "FocusedOverlay", 220 Anchor = Anchor.Centre, 221 Origin = Anchor.Centre, 222 }, 223 stateText = new SpriteText 224 { 225 Text = "FocusedOverlay", 226 Anchor = Anchor.BottomCentre, 227 Origin = Anchor.BottomCentre, 228 } 229 }; 230 231 this.FadeTo(0.2f); 232 } 233 234 protected override void PopIn() 235 { 236 base.PopIn(); 237 stateText.Text = State.ToString(); 238 } 239 240 protected override void PopOut() 241 { 242 base.PopOut(); 243 stateText.Text = State.ToString(); 244 } 245 246 public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; 247 248 protected override bool OnClick(ClickEvent e) 249 { 250 if (!box.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) 251 { 252 Hide(); 253 return true; 254 } 255 256 return base.OnClick(e); 257 } 258 259 protected override void OnFocus(FocusEvent e) 260 { 261 base.OnFocus(e); 262 this.FadeTo(1); 263 } 264 265 protected override void OnFocusLost(FocusLostEvent e) 266 { 267 base.OnFocusLost(e); 268 this.FadeTo(0.2f); 269 } 270 } 271 272 public class RequestingFocusBox : FocusBox 273 { 274 public override bool RequestsFocus => true; 275 276 public RequestingFocusBox() 277 { 278 Box.Colour = Color4.Green; 279 280 AddInternal(new SpriteText 281 { 282 Text = "RequestsFocus", 283 Anchor = Anchor.Centre, 284 Origin = Anchor.Centre, 285 }); 286 } 287 } 288 289 public class FocusBox : CompositeDrawable 290 { 291 protected Box Box; 292 public int KeyDownCount, KeyUpCount, JoystickPressCount, JoystickReleaseCount; 293 294 public FocusBox() 295 { 296 AddInternal(Box = new Box 297 { 298 RelativeSizeAxes = Axes.Both, 299 Alpha = 0.5f, 300 Colour = Color4.Red 301 }); 302 303 RelativeSizeAxes = Axes.Both; 304 Size = new Vector2(0.4f); 305 } 306 307 protected override bool OnClick(ClickEvent e) => true; 308 309 public override bool AcceptsFocus => true; 310 311 protected override void OnFocus(FocusEvent e) 312 { 313 base.OnFocus(e); 314 Box.FadeTo(1); 315 } 316 317 protected override void OnFocusLost(FocusLostEvent e) 318 { 319 base.OnFocusLost(e); 320 Box.FadeTo(0.5f); 321 } 322 323 // only KeyDown is blocking 324 protected override bool OnKeyDown(KeyDownEvent e) 325 { 326 ++KeyDownCount; 327 return true; 328 } 329 330 protected override void OnKeyUp(KeyUpEvent e) 331 { 332 ++KeyUpCount; 333 base.OnKeyUp(e); 334 } 335 336 protected override bool OnJoystickPress(JoystickPressEvent e) 337 { 338 ++JoystickPressCount; 339 return base.OnJoystickPress(e); 340 } 341 342 protected override void OnJoystickRelease(JoystickReleaseEvent e) 343 { 344 ++JoystickReleaseCount; 345 base.OnJoystickRelease(e); 346 } 347 } 348 } 349}