2019-01-24 16:43:03 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
2018-04-13 17:19:50 +08:00
2022-06-17 15:37:17 +08:00
#nullable disable
2018-03-02 14:34:31 +08:00
using NUnit.Framework ;
2018-01-16 19:35:39 +08:00
using osu.Framework.Extensions.IEnumerableExtensions ;
2018-01-16 14:28:00 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Cursor ;
using osu.Framework.Graphics.Shapes ;
2018-10-02 11:02:47 +08:00
using osu.Framework.Input.Events ;
2020-01-09 12:43:44 +08:00
using osu.Framework.Utils ;
2018-01-16 14:28:00 +08:00
using osu.Game.Graphics.Cursor ;
using osu.Game.Graphics.Sprites ;
2018-11-20 15:51:59 +08:00
using osuTK ;
using osuTK.Graphics ;
2022-10-15 05:18:50 +08:00
using osuTK.Input ;
2018-04-13 17:19:50 +08:00
2019-03-25 00:02:36 +08:00
namespace osu.Game.Tests.Visual.UserInterface
2018-01-16 14:28:00 +08:00
{
2018-03-02 14:34:31 +08:00
[TestFixture]
2020-03-23 09:01:33 +08:00
public partial class TestSceneCursors : OsuManualInputManagerTestScene
2018-01-16 14:28:00 +08:00
{
2022-07-26 13:11:52 +08:00
private readonly GlobalCursorDisplay globalCursorDisplay ;
2018-01-16 14:28:00 +08:00
private readonly CustomCursorBox [ ] cursorBoxes = new CustomCursorBox [ 6 ] ;
2018-04-13 17:19:50 +08:00
2019-05-15 03:37:25 +08:00
public TestSceneCursors ( )
2018-01-16 14:28:00 +08:00
{
2022-07-26 13:11:52 +08:00
Child = globalCursorDisplay = new GlobalCursorDisplay
2018-01-16 14:28:00 +08:00
{
2018-04-06 14:20:09 +08:00
RelativeSizeAxes = Axes . Both ,
Children = new [ ]
2018-01-16 14:28:00 +08:00
{
2018-04-06 14:20:09 +08:00
// Middle user
cursorBoxes [ 0 ] = new CustomCursorBox ( Color4 . Green )
2018-01-16 14:28:00 +08:00
{
2018-04-06 14:20:09 +08:00
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
RelativeSizeAxes = Axes . Both ,
Size = new Vector2 ( 0.5f ) ,
} ,
// Top-left user
cursorBoxes [ 1 ] = new CustomCursorBox ( Color4 . Blue )
{
RelativeSizeAxes = Axes . Both ,
Size = new Vector2 ( 0.4f )
} ,
// Bottom-right user
cursorBoxes [ 2 ] = new CustomCursorBox ( Color4 . Red )
{
Anchor = Anchor . BottomRight ,
Origin = Anchor . BottomRight ,
RelativeSizeAxes = Axes . Both ,
Size = new Vector2 ( 0.4f )
} ,
// Bottom-left local
cursorBoxes [ 3 ] = new CustomCursorBox ( Color4 . Magenta , false )
{
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
RelativeSizeAxes = Axes . Both ,
Size = new Vector2 ( 0.4f )
} ,
// Top-right local
cursorBoxes [ 4 ] = new CustomCursorBox ( Color4 . Cyan , false )
{
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
RelativeSizeAxes = Axes . Both ,
Size = new Vector2 ( 0.4f )
} ,
// Left-local
cursorBoxes [ 5 ] = new CustomCursorBox ( Color4 . Yellow , false )
{
Anchor = Anchor . CentreLeft ,
Origin = Anchor . CentreLeft ,
RelativeSizeAxes = Axes . Both ,
Size = new Vector2 ( 0.2f , 1 ) ,
} ,
2018-01-16 14:28:00 +08:00
}
} ;
2018-04-13 17:19:50 +08:00
2018-01-16 19:35:39 +08:00
AddToggleStep ( "Smooth transitions" , b = > cursorBoxes . ForEach ( box = > box . SmoothTransition = b ) ) ;
2018-01-16 14:28:00 +08:00
}
2018-04-13 17:19:50 +08:00
2022-10-15 05:18:50 +08:00
[SetUp]
public void SetUp ( ) = > Schedule ( moveOut ) ;
2018-01-16 14:28:00 +08:00
/// <summary>
/// -- Green Box --
/// Tests whether hovering in and out of a drawable that provides the user cursor (green)
/// results in the correct visibility state for that cursor.
/// </summary>
2022-10-15 05:18:50 +08:00
[Test]
public void TestUserCursor ( )
2018-01-16 14:28:00 +08:00
{
2018-04-06 14:20:09 +08:00
AddStep ( "Move to green area" , ( ) = > InputManager . MoveMouseTo ( cursorBoxes [ 0 ] ) ) ;
2022-10-11 21:20:57 +08:00
AddAssert ( "Check green cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 0 ] . Cursor ) ) ;
AddAssert ( "Check green cursor at mouse" , ( ) = > checkAtMouse ( cursorBoxes [ 0 ] . Cursor ) ) ;
2018-01-16 14:28:00 +08:00
AddStep ( "Move out" , moveOut ) ;
2022-10-11 21:20:57 +08:00
AddAssert ( "Check green cursor invisible" , ( ) = > ! checkVisible ( cursorBoxes [ 0 ] . Cursor ) ) ;
2022-07-26 13:11:52 +08:00
AddAssert ( "Check global cursor visible" , ( ) = > checkVisible ( globalCursorDisplay . MenuCursor ) ) ;
2018-01-16 14:28:00 +08:00
}
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
/// <summary>
/// -- Purple Box --
/// Tests whether hovering in and out of a drawable that provides a local cursor (purple)
/// results in the correct visibility and state for that cursor.
/// </summary>
2022-10-15 05:18:50 +08:00
[Test]
public void TestLocalCursor ( )
2018-01-16 14:28:00 +08:00
{
2018-04-06 14:20:09 +08:00
AddStep ( "Move to purple area" , ( ) = > InputManager . MoveMouseTo ( cursorBoxes [ 3 ] ) ) ;
2022-10-11 21:20:57 +08:00
AddAssert ( "Check purple cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 3 ] . Cursor ) ) ;
AddAssert ( "Check purple cursor at mouse" , ( ) = > checkAtMouse ( cursorBoxes [ 3 ] . Cursor ) ) ;
2022-07-26 13:11:52 +08:00
AddAssert ( "Check global cursor visible" , ( ) = > checkVisible ( globalCursorDisplay . MenuCursor ) ) ;
AddAssert ( "Check global cursor at mouse" , ( ) = > checkAtMouse ( globalCursorDisplay . MenuCursor ) ) ;
2018-01-16 14:28:00 +08:00
AddStep ( "Move out" , moveOut ) ;
2022-10-11 21:20:57 +08:00
AddAssert ( "Check purple cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 3 ] . Cursor ) ) ;
2022-07-26 13:11:52 +08:00
AddAssert ( "Check global cursor visible" , ( ) = > checkVisible ( globalCursorDisplay . MenuCursor ) ) ;
2018-01-16 14:28:00 +08:00
}
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
/// <summary>
/// -- Blue-Green Box Boundary --
/// Tests whether overriding a user cursor (green) with another user cursor (blue)
/// results in the correct visibility and states for the cursors.
/// </summary>
2022-10-15 05:18:50 +08:00
[Test]
public void TestUserCursorOverride ( )
2018-01-16 14:28:00 +08:00
{
2018-04-06 14:20:09 +08:00
AddStep ( "Move to blue-green boundary" , ( ) = > InputManager . MoveMouseTo ( cursorBoxes [ 1 ] . ScreenSpaceDrawQuad . BottomRight - new Vector2 ( 10 ) ) ) ;
2022-10-11 21:20:57 +08:00
AddAssert ( "Check blue cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 1 ] . Cursor ) ) ;
AddAssert ( "Check green cursor invisible" , ( ) = > ! checkVisible ( cursorBoxes [ 0 ] . Cursor ) ) ;
AddAssert ( "Check blue cursor at mouse" , ( ) = > checkAtMouse ( cursorBoxes [ 1 ] . Cursor ) ) ;
2018-01-16 14:28:00 +08:00
AddStep ( "Move out" , moveOut ) ;
2022-10-11 21:20:57 +08:00
AddAssert ( "Check blue cursor not visible" , ( ) = > ! checkVisible ( cursorBoxes [ 1 ] . Cursor ) ) ;
AddAssert ( "Check green cursor not visible" , ( ) = > ! checkVisible ( cursorBoxes [ 0 ] . Cursor ) ) ;
2018-01-16 14:28:00 +08:00
}
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
/// <summary>
/// -- Yellow-Purple Box Boundary --
/// Tests whether multiple local cursors (purple + yellow) may be visible and at the mouse position at the same time.
/// </summary>
2022-10-15 05:18:50 +08:00
[Test]
public void TestMultipleLocalCursors ( )
2018-01-16 14:28:00 +08:00
{
2018-04-06 14:20:09 +08:00
AddStep ( "Move to yellow-purple boundary" , ( ) = > InputManager . MoveMouseTo ( cursorBoxes [ 5 ] . ScreenSpaceDrawQuad . BottomRight - new Vector2 ( 10 ) ) ) ;
2022-10-11 21:20:57 +08:00
AddAssert ( "Check purple cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 3 ] . Cursor ) ) ;
AddAssert ( "Check purple cursor at mouse" , ( ) = > checkAtMouse ( cursorBoxes [ 3 ] . Cursor ) ) ;
AddAssert ( "Check yellow cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 5 ] . Cursor ) ) ;
AddAssert ( "Check yellow cursor at mouse" , ( ) = > checkAtMouse ( cursorBoxes [ 5 ] . Cursor ) ) ;
2018-01-16 14:28:00 +08:00
AddStep ( "Move out" , moveOut ) ;
2022-10-11 21:20:57 +08:00
AddAssert ( "Check purple cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 3 ] . Cursor ) ) ;
AddAssert ( "Check yellow cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 5 ] . Cursor ) ) ;
2018-01-16 14:28:00 +08:00
}
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
/// <summary>
/// -- Yellow-Blue Box Boundary --
/// Tests whether a local cursor (yellow) may be displayed along with a user cursor override (blue).
/// </summary>
2022-10-15 05:18:50 +08:00
[Test]
public void TestUserOverrideWithLocal ( )
2018-01-16 14:28:00 +08:00
{
2022-10-15 05:41:14 +08:00
AddStep ( "Move to yellow-blue boundary" , ( ) = > InputManager . MoveMouseTo ( cursorBoxes [ 5 ] . ScreenSpaceDrawQuad . TopRight - new Vector2 ( 10 , 0 ) ) ) ;
2022-10-11 21:20:57 +08:00
AddAssert ( "Check blue cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 1 ] . Cursor ) ) ;
AddAssert ( "Check blue cursor at mouse" , ( ) = > checkAtMouse ( cursorBoxes [ 1 ] . Cursor ) ) ;
AddAssert ( "Check yellow cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 5 ] . Cursor ) ) ;
AddAssert ( "Check yellow cursor at mouse" , ( ) = > checkAtMouse ( cursorBoxes [ 5 ] . Cursor ) ) ;
2018-01-16 14:28:00 +08:00
AddStep ( "Move out" , moveOut ) ;
2022-10-11 21:20:57 +08:00
AddAssert ( "Check blue cursor invisible" , ( ) = > ! checkVisible ( cursorBoxes [ 1 ] . Cursor ) ) ;
AddAssert ( "Check yellow cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 5 ] . Cursor ) ) ;
2018-01-16 14:28:00 +08:00
}
2018-04-13 17:19:50 +08:00
2022-10-15 05:18:50 +08:00
/// <summary>
/// Ensures non-mouse input hides global cursor on a "local cursor" area (which doesn't hide global cursor).
/// </summary>
[Test]
public void TestKeyboardLocalCursor ( [ Values ] bool clickToShow )
{
2022-10-20 09:06:33 +08:00
AddStep ( "Enable cursor hiding" , ( ) = > globalCursorDisplay . MenuCursor . HideCursorOnNonMouseInput = true ) ;
2022-10-15 05:18:50 +08:00
AddStep ( "Move to purple area" , ( ) = > InputManager . MoveMouseTo ( cursorBoxes [ 3 ] . ScreenSpaceDrawQuad . Centre + new Vector2 ( 10 , 0 ) ) ) ;
AddAssert ( "Check purple cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 3 ] . Cursor ) ) ;
AddAssert ( "Check global cursor alpha is 1" , ( ) = > globalCursorDisplay . MenuCursor . Alpha = = 1 ) ;
AddStep ( "Press key" , ( ) = > InputManager . Key ( Key . A ) ) ;
AddAssert ( "Check purple cursor still visible" , ( ) = > checkVisible ( cursorBoxes [ 3 ] . Cursor ) ) ;
AddUntilStep ( "Check global cursor alpha is 0" , ( ) = > globalCursorDisplay . MenuCursor . ActiveCursor . Alpha = = 0 ) ;
if ( clickToShow )
AddStep ( "Click mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
else
AddStep ( "Move mouse" , ( ) = > InputManager . MoveMouseTo ( InputManager . CurrentState . Mouse . Position + Vector2 . One ) ) ;
AddAssert ( "Check purple cursor still visible" , ( ) = > checkVisible ( cursorBoxes [ 3 ] . Cursor ) ) ;
AddUntilStep ( "Check global cursor alpha is 1" , ( ) = > globalCursorDisplay . MenuCursor . ActiveCursor . Alpha = = 1 ) ;
}
/// <summary>
/// Ensures mouse input after non-mouse input doesn't show global cursor on a "user cursor" area (which hides global cursor).
/// </summary>
[Test]
public void TestKeyboardUserCursor ( [ Values ] bool clickToShow )
{
2022-10-20 09:06:33 +08:00
AddStep ( "Enable cursor hiding" , ( ) = > globalCursorDisplay . MenuCursor . HideCursorOnNonMouseInput = true ) ;
2022-10-15 05:18:50 +08:00
AddStep ( "Move to green area" , ( ) = > InputManager . MoveMouseTo ( cursorBoxes [ 0 ] ) ) ;
AddAssert ( "Check green cursor visible" , ( ) = > checkVisible ( cursorBoxes [ 0 ] . Cursor ) ) ;
AddAssert ( "Check global cursor alpha is 0" , ( ) = > ! checkVisible ( globalCursorDisplay . MenuCursor ) & & globalCursorDisplay . MenuCursor . ActiveCursor . Alpha = = 0 ) ;
AddStep ( "Press key" , ( ) = > InputManager . Key ( Key . A ) ) ;
AddAssert ( "Check green cursor still visible" , ( ) = > checkVisible ( cursorBoxes [ 0 ] . Cursor ) ) ;
AddAssert ( "Check global cursor alpha is still 0" , ( ) = > ! checkVisible ( globalCursorDisplay . MenuCursor ) & & globalCursorDisplay . MenuCursor . ActiveCursor . Alpha = = 0 ) ;
if ( clickToShow )
AddStep ( "Click mouse" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
else
AddStep ( "Move mouse" , ( ) = > InputManager . MoveMouseTo ( InputManager . CurrentState . Mouse . Position + Vector2 . One ) ) ;
AddAssert ( "Check green cursor still visible" , ( ) = > checkVisible ( cursorBoxes [ 0 ] . Cursor ) ) ;
AddAssert ( "Check global cursor alpha is still 0" , ( ) = > ! checkVisible ( globalCursorDisplay . MenuCursor ) & & globalCursorDisplay . MenuCursor . ActiveCursor . Alpha = = 0 ) ;
}
2018-01-16 14:28:00 +08:00
/// <summary>
/// Moves the cursor to a point not covered by any cursor containers.
/// </summary>
private void moveOut ( )
2018-04-06 14:20:09 +08:00
= > InputManager . MoveMouseTo ( new Vector2 ( InputManager . ScreenSpaceDrawQuad . Centre . X , InputManager . ScreenSpaceDrawQuad . TopLeft . Y ) ) ;
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
/// <summary>
/// Checks if a cursor is visible.
/// </summary>
/// <param name="cursorContainer">The cursor to check.</param>
2019-06-11 13:28:52 +08:00
private bool checkVisible ( CursorContainer cursorContainer ) = > cursorContainer . State . Value = = Visibility . Visible ;
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
/// <summary>
/// Checks if a cursor is at the current inputmanager screen position.
/// </summary>
/// <param name="cursorContainer">The cursor to check.</param>
private bool checkAtMouse ( CursorContainer cursorContainer )
2018-09-19 19:52:57 +08:00
= > Precision . AlmostEquals ( InputManager . CurrentState . Mouse . Position , cursorContainer . ToScreenSpace ( cursorContainer . ActiveCursor . DrawPosition ) ) ;
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
private partial class CustomCursorBox : Container , IProvideCursor
{
2018-01-16 19:35:39 +08:00
public bool SmoothTransition ;
2018-04-13 17:19:50 +08:00
2022-10-11 21:20:57 +08:00
public CursorContainer Cursor { get ; }
2018-01-16 14:28:00 +08:00
public bool ProvidingUserCursor { get ; }
2018-04-13 17:19:50 +08:00
2019-06-11 16:28:16 +08:00
public override bool ReceivePositionalInputAt ( Vector2 screenSpacePos ) = > base . ReceivePositionalInputAt ( screenSpacePos ) | | ( SmoothTransition & & ! ProvidingUserCursor ) ;
2018-04-13 17:19:50 +08:00
2018-01-16 19:35:07 +08:00
private readonly Box background ;
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
public CustomCursorBox ( Color4 cursorColour , bool providesUserCursor = true )
{
ProvidingUserCursor = providesUserCursor ;
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
Colour = cursorColour ;
Masking = true ;
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
Children = new Drawable [ ]
{
2018-01-16 19:35:07 +08:00
background = new Box
2018-01-16 14:28:00 +08:00
{
RelativeSizeAxes = Axes . Both ,
Alpha = 0.1f
} ,
new OsuSpriteText
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Text = providesUserCursor ? "User cursor" : "Local cursor"
} ,
2022-10-11 21:20:57 +08:00
Cursor = new TestCursorContainer
2018-01-16 14:28:00 +08:00
{
2019-06-11 13:28:52 +08:00
State = { Value = providesUserCursor ? Visibility . Hidden : Visibility . Visible } ,
2018-01-16 14:28:00 +08:00
}
} ;
}
2018-04-13 17:19:50 +08:00
2018-10-02 11:02:47 +08:00
protected override bool OnHover ( HoverEvent e )
2018-01-16 19:35:07 +08:00
{
background . FadeTo ( 0.4f , 250 , Easing . OutQuint ) ;
return false ;
}
2018-04-13 17:19:50 +08:00
2018-10-02 11:02:47 +08:00
protected override void OnHoverLost ( HoverLostEvent e )
2018-01-16 19:35:07 +08:00
{
background . FadeTo ( 0.1f , 250 ) ;
2018-10-02 11:02:47 +08:00
base . OnHoverLost ( e ) ;
2018-01-16 19:35:07 +08:00
}
2018-01-16 14:28:00 +08:00
}
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
private partial class TestCursorContainer : CursorContainer
{
protected override Drawable CreateCursor ( ) = > new TestCursor ( ) ;
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
private partial class TestCursor : CircularContainer
{
public TestCursor ( )
{
Origin = Anchor . Centre ;
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
Size = new Vector2 ( 50 ) ;
Masking = true ;
2018-04-13 17:19:50 +08:00
2019-08-21 12:29:50 +08:00
Blending = BlendingParameters . Additive ;
2018-01-16 14:28:00 +08:00
Alpha = 0.5f ;
2018-04-13 17:19:50 +08:00
2018-01-16 14:28:00 +08:00
Child = new Box { RelativeSizeAxes = Axes . Both } ;
}
}
}
}
}