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
2017-12-01 23:26:02 +08:00
using System.Collections.Generic ;
2021-07-17 01:30:13 +08:00
using System.Linq ;
2018-03-02 14:34:31 +08:00
using NUnit.Framework ;
2018-01-11 15:51:46 +08:00
using osu.Framework.Allocation ;
2021-07-17 01:30:13 +08:00
using osu.Framework.Graphics ;
2023-08-19 21:08:30 +08:00
using osu.Framework.Graphics.Cursor ;
2021-07-17 01:30:13 +08:00
using osu.Framework.Testing ;
2018-01-30 17:22:40 +08:00
using osu.Game.Beatmaps ;
2021-07-17 01:30:13 +08:00
using osu.Game.Beatmaps.ControlPoints ;
2018-02-22 13:22:04 +08:00
using osu.Game.Rulesets.Edit ;
2021-07-17 01:30:13 +08:00
using osu.Game.Rulesets.Edit.Tools ;
2018-02-20 12:50:55 +08:00
using osu.Game.Rulesets.Objects ;
2018-11-01 14:38:19 +08:00
using osu.Game.Rulesets.Objects.Types ;
2018-02-20 12:50:55 +08:00
using osu.Game.Rulesets.Osu ;
2017-12-01 23:26:02 +08:00
using osu.Game.Rulesets.Osu.Edit ;
using osu.Game.Rulesets.Osu.Objects ;
2024-10-13 23:04:22 +08:00
using osu.Game.Rulesets.UI ;
2020-01-02 00:23:21 +08:00
using osu.Game.Screens.Edit ;
2021-07-17 01:30:13 +08:00
using osu.Game.Screens.Edit.Components.RadioButtons ;
2022-05-04 16:56:38 +08:00
using osu.Game.Screens.Edit.Components.TernaryButtons ;
2021-07-17 01:30:13 +08:00
using osu.Game.Screens.Edit.Compose.Components ;
2019-03-25 00:02:36 +08:00
using osuTK ;
2022-01-18 23:30:31 +08:00
using osuTK.Input ;
2018-04-13 17:19:50 +08:00
2020-04-23 16:07:55 +08:00
namespace osu.Game.Tests.Visual.Editing
2017-12-01 23:26:02 +08:00
{
2018-03-02 14:34:31 +08:00
[TestFixture]
2019-10-25 18:00:10 +08:00
public partial class TestSceneHitObjectComposer : EditorClockTestScene
2017-12-01 23:26:02 +08:00
{
2021-07-17 01:30:13 +08:00
private OsuHitObjectComposer hitObjectComposer ;
private EditorBeatmapContainer editorBeatmapContainer ;
private EditorBeatmap editorBeatmap = > editorBeatmapContainer . EditorBeatmap ;
[SetUpSteps]
public void SetUpSteps ( )
2017-12-01 23:26:02 +08:00
{
2021-07-17 01:30:13 +08:00
AddStep ( "create beatmap" , ( ) = >
2018-01-11 15:51:46 +08:00
{
2021-07-17 01:30:13 +08:00
Beatmap . Value = CreateWorkingBeatmap ( new Beatmap
2018-01-11 15:51:46 +08:00
{
2022-01-12 22:17:35 +08:00
BeatmapInfo =
{
Ruleset = new OsuRuleset ( ) . RulesetInfo
} ,
2021-07-17 01:30:13 +08:00
HitObjects = new List < HitObject >
2018-02-20 12:50:55 +08:00
{
2022-01-12 22:17:35 +08:00
new HitCircle
{
Position = new Vector2 ( 256 , 192 ) , Scale = 0.5f
} ,
2021-07-17 01:30:13 +08:00
new HitCircle { Position = new Vector2 ( 344 , 148 ) , Scale = 0.5f } ,
new Slider
2018-02-20 12:50:55 +08:00
{
2021-07-17 01:30:13 +08:00
Position = new Vector2 ( 128 , 256 ) ,
2023-11-08 18:43:54 +08:00
Path = new SliderPath ( PathType . LINEAR , new [ ]
2021-07-17 01:30:13 +08:00
{
Vector2 . Zero ,
new Vector2 ( 216 , 0 ) ,
} ) ,
Scale = 0.5f ,
}
} ,
} ) ;
} ) ;
AddStep ( "Create composer" , ( ) = >
{
Child = editorBeatmapContainer = new EditorBeatmapContainer ( Beatmap . Value )
{
Child = hitObjectComposer = new OsuHitObjectComposer ( new OsuRuleset ( ) )
2022-05-04 17:56:29 +08:00
{
// force the composer to fully overlap the playfield area by setting a 4:3 aspect ratio.
FillMode = FillMode . Fit ,
FillAspectRatio = 4 / 3f
}
2021-07-17 01:30:13 +08:00
} ;
2018-02-20 12:50:55 +08:00
} ) ;
2021-07-17 01:30:13 +08:00
}
2024-10-13 23:04:22 +08:00
[Test]
public void TestPlacementOutsideComposeScreen ( )
{
AddStep ( "clear all control points and hitobjects" , ( ) = >
{
editorBeatmap . ControlPointInfo . Clear ( ) ;
editorBeatmap . Clear ( ) ;
} ) ;
AddStep ( "Add timing point" , ( ) = > editorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint ( ) ) ) ;
AddStep ( "select circle" , ( ) = > hitObjectComposer . ChildrenOfType < EditorRadioButton > ( ) . First ( d = > d . Button . Label = = "HitCircle" ) . TriggerClick ( ) ) ;
AddStep ( "move mouse to compose" , ( ) = > InputManager . MoveMouseTo ( hitObjectComposer . ChildrenOfType < HitObjectContainer > ( ) . Single ( ) ) ) ;
AddStep ( "click" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
AddAssert ( "circle placed" , ( ) = > editorBeatmap . HitObjects . Count = = 1 ) ;
AddStep ( "move mouse outside compose" , ( ) = > InputManager . MoveMouseTo ( hitObjectComposer . ChildrenOfType < HitObjectContainer > ( ) . Single ( ) . ScreenSpaceDrawQuad . TopLeft - new Vector2 ( 0f , 20f ) ) ) ;
AddStep ( "click" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
AddAssert ( "no circle placed" , ( ) = > editorBeatmap . HitObjects . Count = = 1 ) ;
}
[Test]
public void TestDragSliderOutsideComposeScreen ( )
{
AddStep ( "clear all control points and hitobjects" , ( ) = >
{
editorBeatmap . ControlPointInfo . Clear ( ) ;
editorBeatmap . Clear ( ) ;
} ) ;
AddStep ( "Add timing point" , ( ) = > editorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint ( ) ) ) ;
AddStep ( "select slider" , ( ) = > hitObjectComposer . ChildrenOfType < EditorRadioButton > ( ) . First ( d = > d . Button . Label = = "Slider" ) . TriggerClick ( ) ) ;
AddStep ( "move mouse to compose" , ( ) = > InputManager . MoveMouseTo ( hitObjectComposer . ChildrenOfType < HitObjectContainer > ( ) . Single ( ) ) ) ;
AddStep ( "hold" , ( ) = > InputManager . PressButton ( MouseButton . Left ) ) ;
AddStep ( "move mouse outside compose" , ( ) = > InputManager . MoveMouseTo ( hitObjectComposer . ChildrenOfType < HitObjectContainer > ( ) . Single ( ) . ScreenSpaceDrawQuad . TopLeft - new Vector2 ( 0f , 80f ) ) ) ;
AddStep ( "release" , ( ) = > InputManager . ReleaseButton ( MouseButton . Left ) ) ;
AddAssert ( "slider placed" , ( ) = > editorBeatmap . HitObjects . Count = = 1 ) ;
}
2021-07-17 01:30:13 +08:00
[Test]
public void TestPlacementOnlyWorksWithTiming ( )
{
AddStep ( "clear all control points" , ( ) = > editorBeatmap . ControlPointInfo . Clear ( ) ) ;
AddAssert ( "Tool is selection" , ( ) = > hitObjectComposer . ChildrenOfType < ComposeBlueprintContainer > ( ) . First ( ) . CurrentTool is SelectTool ) ;
2021-07-19 15:57:12 +08:00
AddAssert ( "Hitcircle button not clickable" , ( ) = > ! hitObjectComposer . ChildrenOfType < EditorRadioButton > ( ) . First ( d = > d . Button . Label = = "HitCircle" ) . Enabled . Value ) ;
2021-07-17 01:30:13 +08:00
AddStep ( "Add timing point" , ( ) = > editorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint ( ) ) ) ;
2021-07-19 15:57:12 +08:00
AddAssert ( "Hitcircle button is clickable" , ( ) = > hitObjectComposer . ChildrenOfType < EditorRadioButton > ( ) . First ( d = > d . Button . Label = = "HitCircle" ) . Enabled . Value ) ;
2021-08-04 16:27:44 +08:00
AddStep ( "Change to hitcircle" , ( ) = > hitObjectComposer . ChildrenOfType < EditorRadioButton > ( ) . First ( d = > d . Button . Label = = "HitCircle" ) . TriggerClick ( ) ) ;
2021-07-17 01:30:13 +08:00
AddAssert ( "Tool changed" , ( ) = > hitObjectComposer . ChildrenOfType < ComposeBlueprintContainer > ( ) . First ( ) . CurrentTool is HitCircleCompositionTool ) ;
}
2018-04-13 17:19:50 +08:00
2022-05-04 16:56:38 +08:00
[Test]
public void TestPlacementFailsWhenClickingButton ( )
{
AddStep ( "clear all control points and hitobjects" , ( ) = >
{
editorBeatmap . ControlPointInfo . Clear ( ) ;
editorBeatmap . Clear ( ) ;
} ) ;
AddStep ( "Add timing point" , ( ) = > editorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint ( ) ) ) ;
AddStep ( "Change to hitcircle" , ( ) = > hitObjectComposer . ChildrenOfType < EditorRadioButton > ( ) . First ( d = > d . Button . Label = = "HitCircle" ) . TriggerClick ( ) ) ;
2023-05-03 00:35:06 +08:00
ExpandingToolboxContainer toolboxContainer = null ! ;
AddStep ( "move mouse to toolbox" , ( ) = > InputManager . MoveMouseTo ( toolboxContainer = hitObjectComposer . ChildrenOfType < ExpandingToolboxContainer > ( ) . First ( ) ) ) ;
AddUntilStep ( "toolbox is expanded" , ( ) = > toolboxContainer . Expanded . Value ) ;
AddUntilStep ( "wait for toolbox to expand" , ( ) = > toolboxContainer . LatestTransformEndTime , ( ) = > Is . EqualTo ( Time . Current ) ) ;
2022-05-04 16:56:38 +08:00
AddStep ( "move mouse to overlapping toggle button" , ( ) = >
{
var playfield = hitObjectComposer . Playfield . ScreenSpaceDrawQuad ;
2023-07-22 01:08:40 +08:00
var button = toolboxContainer . ChildrenOfType < DrawableTernaryButton > ( ) . First ( b = > playfield . Contains ( getOverlapPoint ( b ) ) ) ;
2022-05-04 16:56:38 +08:00
2023-07-22 01:08:40 +08:00
InputManager . MoveMouseTo ( getOverlapPoint ( button ) ) ;
2022-05-04 16:56:38 +08:00
} ) ;
AddAssert ( "no circles placed" , ( ) = > editorBeatmap . HitObjects . Count = = 0 ) ;
AddStep ( "attempt place circle" , ( ) = > InputManager . Click ( MouseButton . Left ) ) ;
AddAssert ( "no circles placed" , ( ) = > editorBeatmap . HitObjects . Count = = 0 ) ;
2023-07-22 01:08:40 +08:00
Vector2 getOverlapPoint ( DrawableTernaryButton ternaryButton )
{
var quad = ternaryButton . ScreenSpaceDrawQuad ;
return quad . TopLeft + new Vector2 ( quad . Width * 9 / 10 , quad . Height / 2 ) ;
}
2022-05-04 16:56:38 +08:00
}
[Test]
public void TestPlacementWithinToolboxScrollArea ( )
{
AddStep ( "clear all control points and hitobjects" , ( ) = >
{
editorBeatmap . ControlPointInfo . Clear ( ) ;
editorBeatmap . Clear ( ) ;
} ) ;
AddStep ( "Add timing point" , ( ) = > editorBeatmap . ControlPointInfo . Add ( 0 , new TimingControlPoint ( ) ) ) ;
AddStep ( "Change to hitcircle" , ( ) = > hitObjectComposer . ChildrenOfType < EditorRadioButton > ( ) . First ( d = > d . Button . Label = = "HitCircle" ) . TriggerClick ( ) ) ;
AddStep ( "move mouse to scroll area" , ( ) = >
{
// Specifically wanting to test the area of overlap between the left expanding toolbox container
// and the playfield/composer.
var scrollArea = hitObjectComposer . ChildrenOfType < ExpandingToolboxContainer > ( ) . First ( ) . ScreenSpaceDrawQuad ;
var playfield = hitObjectComposer . Playfield . ScreenSpaceDrawQuad ;
2022-05-04 17:56:44 +08:00
InputManager . MoveMouseTo ( new Vector2 ( scrollArea . TopLeft . X + 1 , playfield . Centre . Y ) ) ;
2022-05-04 16:56:38 +08:00
} ) ;
AddAssert ( "no circles placed" , ( ) = > editorBeatmap . HitObjects . Count = = 0 ) ;
}
2022-01-18 23:30:31 +08:00
[Test]
public void TestDistanceSpacingHotkeys ( )
{
2022-04-28 11:52:42 +08:00
double originalSpacing = 0 ;
2022-01-18 23:30:31 +08:00
2022-04-28 11:52:42 +08:00
AddStep ( "retrieve original spacing" , ( ) = > originalSpacing = editorBeatmap . BeatmapInfo . DistanceSpacing ) ;
2022-01-18 23:30:31 +08:00
2022-04-27 22:11:23 +08:00
AddStep ( "hold ctrl" , ( ) = > InputManager . PressKey ( Key . LControl ) ) ;
2022-01-18 23:30:31 +08:00
AddStep ( "hold alt" , ( ) = > InputManager . PressKey ( Key . LAlt ) ) ;
AddStep ( "scroll mouse 5 steps" , ( ) = > InputManager . ScrollVerticalBy ( 5 ) ) ;
AddStep ( "release alt" , ( ) = > InputManager . ReleaseKey ( Key . LAlt ) ) ;
2022-04-27 22:11:23 +08:00
AddStep ( "release ctrl" , ( ) = > InputManager . ReleaseKey ( Key . LControl ) ) ;
2022-10-19 22:56:42 +08:00
AddAssert ( "distance spacing increased by 0.5" , ( ) = > editorBeatmap . BeatmapInfo . DistanceSpacing = = originalSpacing + 0.5 ) ;
2022-01-18 23:30:31 +08:00
}
2023-08-19 21:08:30 +08:00
public partial class EditorBeatmapContainer : PopoverContainer
2021-07-17 01:30:13 +08:00
{
2021-11-15 17:46:11 +08:00
private readonly IWorkingBeatmap working ;
2021-07-17 01:30:13 +08:00
public EditorBeatmap EditorBeatmap { get ; private set ; }
2021-11-15 17:46:11 +08:00
public EditorBeatmapContainer ( IWorkingBeatmap working )
2021-07-17 01:30:13 +08:00
{
this . working = working ;
RelativeSizeAxes = Axes . Both ;
}
2020-01-02 00:23:21 +08:00
2021-07-17 01:30:13 +08:00
protected override IReadOnlyDependencyContainer CreateChildDependencies ( IReadOnlyDependencyContainer parent )
{
var dependencies = new DependencyContainer ( base . CreateChildDependencies ( parent ) ) ;
EditorBeatmap = new EditorBeatmap ( working . GetPlayableBeatmap ( new OsuRuleset ( ) . RulesetInfo ) ) ;
dependencies . CacheAs ( EditorBeatmap ) ;
dependencies . CacheAs < IBeatSnapProvider > ( EditorBeatmap ) ;
return dependencies ;
}
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2018-04-13 17:19:50 +08:00
2021-07-17 01:30:13 +08:00
Add ( EditorBeatmap ) ;
}
2018-10-17 16:41:17 +08:00
}
2017-12-01 23:26:02 +08:00
}
}