2021-11-13 15:58:24 +00: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.
using System.Linq ;
2023-04-26 15:34:02 +00:00
using System.Collections.Generic ;
2021-11-13 15:58:24 +00:00
using Humanizer ;
using NUnit.Framework ;
using osu.Framework.Testing ;
2022-10-19 11:34:41 +00:00
using osu.Game.Audio ;
2021-11-13 15:58:24 +00:00
using osu.Game.Beatmaps ;
2021-11-13 16:43:33 +00:00
using osu.Game.Graphics.UserInterface ;
2021-11-13 15:58:24 +00:00
using osu.Game.Graphics.UserInterfaceV2 ;
using osu.Game.Rulesets ;
2023-05-24 08:33:48 +00:00
using osu.Game.Rulesets.Edit ;
2021-11-13 15:58:24 +00:00
using osu.Game.Rulesets.Osu ;
using osu.Game.Rulesets.Osu.Objects ;
using osu.Game.Rulesets.Osu.UI ;
using osu.Game.Screens.Edit.Compose.Components.Timeline ;
using osu.Game.Screens.Edit.Timing ;
using osu.Game.Tests.Beatmaps ;
using osuTK ;
using osuTK.Input ;
namespace osu.Game.Tests.Visual.Editing
{
2023-05-16 09:31:10 +00:00
public partial class TestSceneHitObjectSampleAdjustments : EditorTestScene
2021-11-13 15:58:24 +00:00
{
protected override Ruleset CreateEditorRuleset ( ) = > new OsuRuleset ( ) ;
protected override IBeatmap CreateBeatmap ( RulesetInfo ruleset ) = > new TestBeatmap ( ruleset , false ) ;
public override void SetUpSteps ( )
{
base . SetUpSteps ( ) ;
AddStep ( "add test objects" , ( ) = >
{
EditorBeatmap . Add ( new HitCircle
{
StartTime = 0 ,
Position = ( OsuPlayfield . BASE_SIZE - new Vector2 ( 100 , 0 ) ) / 2 ,
2023-04-26 12:21:52 +00:00
Samples = new List < HitSampleInfo >
2021-11-13 15:58:24 +00:00
{
2023-05-16 07:29:24 +00:00
new HitSampleInfo ( HitSampleInfo . HIT_NORMAL , volume : 80 )
2021-11-13 15:58:24 +00:00
}
} ) ;
2021-11-13 16:43:33 +00:00
EditorBeatmap . Add ( new HitCircle
2021-11-13 15:58:24 +00:00
{
StartTime = 500 ,
Position = ( OsuPlayfield . BASE_SIZE + new Vector2 ( 100 , 0 ) ) / 2 ,
2023-04-26 12:21:52 +00:00
Samples = new List < HitSampleInfo >
2021-11-13 15:58:24 +00:00
{
2023-05-24 05:04:10 +00:00
new HitSampleInfo ( HitSampleInfo . HIT_NORMAL , HitSampleInfo . BANK_SOFT , volume : 60 )
2021-11-13 15:58:24 +00:00
}
} ) ;
} ) ;
}
2023-05-16 09:52:35 +00:00
[Test]
public void TestAddSampleAddition ( )
{
AddStep ( "select both objects" , ( ) = > EditorBeatmap . SelectedHitObjects . AddRange ( EditorBeatmap . HitObjects ) ) ;
AddStep ( "add clap addition" , ( ) = > InputManager . Key ( Key . R ) ) ;
hitObjectHasSampleBank ( 0 , "normal" ) ;
hitObjectHasSamples ( 0 , HitSampleInfo . HIT_NORMAL , HitSampleInfo . HIT_CLAP ) ;
2023-05-24 05:04:10 +00:00
hitObjectHasSampleBank ( 1 , HitSampleInfo . BANK_SOFT ) ;
2023-05-16 09:52:35 +00:00
hitObjectHasSamples ( 1 , HitSampleInfo . HIT_NORMAL , HitSampleInfo . HIT_CLAP ) ;
AddStep ( "remove clap addition" , ( ) = > InputManager . Key ( Key . R ) ) ;
hitObjectHasSampleBank ( 0 , "normal" ) ;
hitObjectHasSamples ( 0 , HitSampleInfo . HIT_NORMAL ) ;
2023-05-24 05:04:10 +00:00
hitObjectHasSampleBank ( 1 , HitSampleInfo . BANK_SOFT ) ;
2023-05-16 09:52:35 +00:00
hitObjectHasSamples ( 1 , HitSampleInfo . HIT_NORMAL ) ;
}
2022-05-11 06:51:59 +00:00
[Test]
public void TestPopoverHasFocus ( )
{
clickSamplePiece ( 0 ) ;
samplePopoverHasFocus ( ) ;
}
2021-11-13 15:58:24 +00:00
[Test]
public void TestSingleSelection ( )
{
clickSamplePiece ( 0 ) ;
2022-10-19 11:34:41 +00:00
samplePopoverHasSingleBank ( HitSampleInfo . BANK_NORMAL ) ;
2021-11-13 15:58:24 +00:00
samplePopoverHasSingleVolume ( 80 ) ;
dismissPopover ( ) ;
// select first object to ensure that sample pieces for unselected objects
// work independently from selection state.
AddStep ( "select first object" , ( ) = > EditorBeatmap . SelectedHitObjects . Add ( EditorBeatmap . HitObjects . First ( ) ) ) ;
clickSamplePiece ( 1 ) ;
2022-10-19 11:34:41 +00:00
samplePopoverHasSingleBank ( HitSampleInfo . BANK_SOFT ) ;
2021-11-13 15:58:24 +00:00
samplePopoverHasSingleVolume ( 60 ) ;
setVolumeViaPopover ( 90 ) ;
hitObjectHasSampleVolume ( 1 , 90 ) ;
2022-10-19 11:34:41 +00:00
setBankViaPopover ( HitSampleInfo . BANK_DRUM ) ;
hitObjectHasSampleBank ( 1 , HitSampleInfo . BANK_DRUM ) ;
2021-11-13 15:58:24 +00:00
}
2023-05-22 20:23:05 +00:00
[Test]
public void TestUndo ( )
{
clickSamplePiece ( 1 ) ;
2023-05-24 05:04:10 +00:00
samplePopoverHasSingleBank ( HitSampleInfo . BANK_SOFT ) ;
2023-05-22 20:23:05 +00:00
samplePopoverHasSingleVolume ( 60 ) ;
setVolumeViaPopover ( 90 ) ;
hitObjectHasSampleVolume ( 1 , 90 ) ;
dismissPopover ( ) ;
AddStep ( "undo" , ( ) = > Editor . Undo ( ) ) ;
hitObjectHasSampleVolume ( 1 , 60 ) ;
}
2021-11-13 16:02:55 +00:00
[Test]
public void TestMultipleSelectionWithSameSampleVolume ( )
{
AddStep ( "unify sample volume" , ( ) = >
{
foreach ( var h in EditorBeatmap . HitObjects )
2023-04-26 12:21:52 +00:00
{
for ( int i = 0 ; i < h . Samples . Count ; i + + )
{
h . Samples [ i ] = h . Samples [ i ] . With ( newVolume : 50 ) ;
}
}
2021-11-13 16:02:55 +00:00
} ) ;
AddStep ( "select both objects" , ( ) = > EditorBeatmap . SelectedHitObjects . AddRange ( EditorBeatmap . HitObjects ) ) ;
clickSamplePiece ( 0 ) ;
samplePopoverHasSingleVolume ( 50 ) ;
dismissPopover ( ) ;
clickSamplePiece ( 1 ) ;
samplePopoverHasSingleVolume ( 50 ) ;
setVolumeViaPopover ( 75 ) ;
hitObjectHasSampleVolume ( 0 , 75 ) ;
hitObjectHasSampleVolume ( 1 , 75 ) ;
}
[Test]
public void TestMultipleSelectionWithDifferentSampleVolume ( )
{
AddStep ( "select both objects" , ( ) = > EditorBeatmap . SelectedHitObjects . AddRange ( EditorBeatmap . HitObjects ) ) ;
clickSamplePiece ( 0 ) ;
samplePopoverHasIndeterminateVolume ( ) ;
dismissPopover ( ) ;
clickSamplePiece ( 1 ) ;
samplePopoverHasIndeterminateVolume ( ) ;
setVolumeViaPopover ( 30 ) ;
hitObjectHasSampleVolume ( 0 , 30 ) ;
hitObjectHasSampleVolume ( 1 , 30 ) ;
}
2021-11-13 16:07:19 +00:00
[Test]
2023-05-24 08:33:48 +00:00
public void TestPopoverMultipleSelectionWithSameSampleBank ( )
2021-11-13 16:07:19 +00:00
{
AddStep ( "unify sample bank" , ( ) = >
{
foreach ( var h in EditorBeatmap . HitObjects )
2023-04-26 12:21:52 +00:00
{
for ( int i = 0 ; i < h . Samples . Count ; i + + )
{
2023-05-24 05:04:10 +00:00
h . Samples [ i ] = h . Samples [ i ] . With ( newBank : HitSampleInfo . BANK_SOFT ) ;
2023-04-26 12:21:52 +00:00
}
}
2021-11-13 16:07:19 +00:00
} ) ;
AddStep ( "select both objects" , ( ) = > EditorBeatmap . SelectedHitObjects . AddRange ( EditorBeatmap . HitObjects ) ) ;
clickSamplePiece ( 0 ) ;
2022-10-19 11:34:41 +00:00
samplePopoverHasSingleBank ( HitSampleInfo . BANK_SOFT ) ;
2021-11-13 16:07:19 +00:00
dismissPopover ( ) ;
clickSamplePiece ( 1 ) ;
2022-10-19 11:34:41 +00:00
samplePopoverHasSingleBank ( HitSampleInfo . BANK_SOFT ) ;
2021-11-13 16:07:19 +00:00
2021-11-13 16:43:33 +00:00
setBankViaPopover ( string . Empty ) ;
2022-10-19 11:34:41 +00:00
hitObjectHasSampleBank ( 0 , HitSampleInfo . BANK_SOFT ) ;
hitObjectHasSampleBank ( 1 , HitSampleInfo . BANK_SOFT ) ;
samplePopoverHasSingleBank ( HitSampleInfo . BANK_SOFT ) ;
setBankViaPopover ( HitSampleInfo . BANK_DRUM ) ;
hitObjectHasSampleBank ( 0 , HitSampleInfo . BANK_DRUM ) ;
hitObjectHasSampleBank ( 1 , HitSampleInfo . BANK_DRUM ) ;
samplePopoverHasSingleBank ( HitSampleInfo . BANK_DRUM ) ;
2021-11-13 16:07:19 +00:00
}
[Test]
2023-05-24 08:33:48 +00:00
public void TestPopoverMultipleSelectionWithDifferentSampleBank ( )
2021-11-13 16:07:19 +00:00
{
AddStep ( "select both objects" , ( ) = > EditorBeatmap . SelectedHitObjects . AddRange ( EditorBeatmap . HitObjects ) ) ;
clickSamplePiece ( 0 ) ;
samplePopoverHasIndeterminateBank ( ) ;
dismissPopover ( ) ;
clickSamplePiece ( 1 ) ;
samplePopoverHasIndeterminateBank ( ) ;
2021-11-13 16:43:33 +00:00
setBankViaPopover ( string . Empty ) ;
2022-10-19 11:34:41 +00:00
hitObjectHasSampleBank ( 0 , HitSampleInfo . BANK_NORMAL ) ;
hitObjectHasSampleBank ( 1 , HitSampleInfo . BANK_SOFT ) ;
2021-11-13 16:43:33 +00:00
samplePopoverHasIndeterminateBank ( ) ;
2022-10-19 11:34:41 +00:00
setBankViaPopover ( HitSampleInfo . BANK_NORMAL ) ;
hitObjectHasSampleBank ( 0 , HitSampleInfo . BANK_NORMAL ) ;
hitObjectHasSampleBank ( 1 , HitSampleInfo . BANK_NORMAL ) ;
samplePopoverHasSingleBank ( HitSampleInfo . BANK_NORMAL ) ;
2021-11-13 16:07:19 +00:00
}
2023-05-24 08:33:48 +00:00
[Test]
public void TestHotkeysMultipleSelectionWithSameSampleBank ( )
{
AddStep ( "unify sample bank" , ( ) = >
{
foreach ( var h in EditorBeatmap . HitObjects )
{
for ( int i = 0 ; i < h . Samples . Count ; i + + )
{
h . Samples [ i ] = h . Samples [ i ] . With ( newBank : HitSampleInfo . BANK_SOFT ) ;
}
}
} ) ;
AddStep ( "select both objects" , ( ) = > EditorBeatmap . SelectedHitObjects . AddRange ( EditorBeatmap . HitObjects ) ) ;
hitObjectHasSampleBank ( 0 , HitSampleInfo . BANK_SOFT ) ;
hitObjectHasSampleBank ( 1 , HitSampleInfo . BANK_SOFT ) ;
AddStep ( "Press normal bank shortcut" , ( ) = >
{
InputManager . PressKey ( Key . ShiftLeft ) ;
InputManager . Key ( Key . W ) ;
InputManager . ReleaseKey ( Key . ShiftLeft ) ;
} ) ;
hitObjectHasSampleBank ( 0 , HitSampleInfo . BANK_NORMAL ) ;
hitObjectHasSampleBank ( 1 , HitSampleInfo . BANK_NORMAL ) ;
AddStep ( "Press drum bank shortcut" , ( ) = >
{
InputManager . PressKey ( Key . ShiftLeft ) ;
InputManager . Key ( Key . R ) ;
InputManager . ReleaseKey ( Key . ShiftLeft ) ;
} ) ;
hitObjectHasSampleBank ( 0 , HitSampleInfo . BANK_DRUM ) ;
hitObjectHasSampleBank ( 1 , HitSampleInfo . BANK_DRUM ) ;
AddStep ( "Press auto bank shortcut" , ( ) = >
{
InputManager . PressKey ( Key . ShiftLeft ) ;
InputManager . Key ( Key . Q ) ;
InputManager . ReleaseKey ( Key . ShiftLeft ) ;
} ) ;
// Should be a noop.
hitObjectHasSampleBank ( 0 , HitSampleInfo . BANK_DRUM ) ;
hitObjectHasSampleBank ( 1 , HitSampleInfo . BANK_DRUM ) ;
}
[Test]
public void TestHotkeysDuringPlacement ( )
{
AddStep ( "Enter placement mode" , ( ) = > InputManager . Key ( Key . Number2 ) ) ;
AddStep ( "Move mouse to centre" , ( ) = > InputManager . MoveMouseTo ( Editor . ChildrenOfType < HitObjectComposer > ( ) . First ( ) . ScreenSpaceDrawQuad . Centre ) ) ;
AddStep ( "Move between two objects" , ( ) = > EditorClock . Seek ( 250 ) ) ;
AddStep ( "Press normal bank shortcut" , ( ) = >
{
InputManager . PressKey ( Key . ShiftLeft ) ;
InputManager . Key ( Key . W ) ;
InputManager . ReleaseKey ( Key . ShiftLeft ) ;
} ) ;
checkPlacementSample ( HitSampleInfo . BANK_NORMAL ) ;
AddStep ( "Press drum bank shortcut" , ( ) = >
{
InputManager . PressKey ( Key . ShiftLeft ) ;
InputManager . Key ( Key . R ) ;
InputManager . ReleaseKey ( Key . ShiftLeft ) ;
} ) ;
checkPlacementSample ( HitSampleInfo . BANK_DRUM ) ;
AddStep ( "Press auto bank shortcut" , ( ) = >
{
InputManager . PressKey ( Key . ShiftLeft ) ;
InputManager . Key ( Key . Q ) ;
InputManager . ReleaseKey ( Key . ShiftLeft ) ;
} ) ;
checkPlacementSample ( HitSampleInfo . BANK_NORMAL ) ;
AddStep ( "Move after second object" , ( ) = > EditorClock . Seek ( 750 ) ) ;
checkPlacementSample ( HitSampleInfo . BANK_SOFT ) ;
AddStep ( "Move to first object" , ( ) = > EditorClock . Seek ( 0 ) ) ;
checkPlacementSample ( HitSampleInfo . BANK_NORMAL ) ;
void checkPlacementSample ( string expected ) = > AddAssert ( $"Placement sample is {expected}" , ( ) = > EditorBeatmap . PlacementObject . Value . Samples . First ( ) . Bank , ( ) = > Is . EqualTo ( expected ) ) ;
}
2022-05-11 06:51:00 +00:00
private void clickSamplePiece ( int objectIndex ) = > AddStep ( $"click {objectIndex.ToOrdinalWords()} sample piece" , ( ) = >
2021-11-13 15:58:24 +00:00
{
2022-05-11 06:51:00 +00:00
var samplePiece = this . ChildrenOfType < SamplePointPiece > ( ) . Single ( piece = > piece . HitObject = = EditorBeatmap . HitObjects . ElementAt ( objectIndex ) ) ;
2021-11-13 15:58:24 +00:00
2022-05-11 06:51:00 +00:00
InputManager . MoveMouseTo ( samplePiece ) ;
2021-11-13 15:58:24 +00:00
InputManager . Click ( MouseButton . Left ) ;
} ) ;
2022-05-11 06:51:59 +00:00
private void samplePopoverHasFocus ( ) = > AddUntilStep ( "sample popover textbox focused" , ( ) = >
{
var popover = this . ChildrenOfType < SamplePointPiece . SampleEditPopover > ( ) . SingleOrDefault ( ) ;
var slider = popover ? . ChildrenOfType < IndeterminateSliderWithTextBoxInput < int > > ( ) . Single ( ) ;
var textbox = slider ? . ChildrenOfType < OsuTextBox > ( ) . Single ( ) ;
return textbox ? . HasFocus = = true ;
} ) ;
2021-11-13 15:58:24 +00:00
private void samplePopoverHasSingleVolume ( int volume ) = > AddUntilStep ( $"sample popover has volume {volume}" , ( ) = >
{
var popover = this . ChildrenOfType < SamplePointPiece . SampleEditPopover > ( ) . SingleOrDefault ( ) ;
2021-11-13 16:21:48 +00:00
var slider = popover ? . ChildrenOfType < IndeterminateSliderWithTextBoxInput < int > > ( ) . Single ( ) ;
2021-11-13 15:58:24 +00:00
return slider ? . Current . Value = = volume ;
} ) ;
2021-11-13 16:21:48 +00:00
private void samplePopoverHasIndeterminateVolume ( ) = > AddUntilStep ( "sample popover has indeterminate volume" , ( ) = >
2021-11-13 16:02:55 +00:00
{
var popover = this . ChildrenOfType < SamplePointPiece . SampleEditPopover > ( ) . SingleOrDefault ( ) ;
2021-11-13 16:21:48 +00:00
var slider = popover ? . ChildrenOfType < IndeterminateSliderWithTextBoxInput < int > > ( ) . Single ( ) ;
2021-11-13 16:02:55 +00:00
return slider ! = null & & slider . Current . Value = = null ;
} ) ;
2021-11-13 15:58:24 +00:00
private void samplePopoverHasSingleBank ( string bank ) = > AddUntilStep ( $"sample popover has bank {bank}" , ( ) = >
{
var popover = this . ChildrenOfType < SamplePointPiece . SampleEditPopover > ( ) . SingleOrDefault ( ) ;
2021-11-13 16:43:33 +00:00
var textBox = popover ? . ChildrenOfType < OsuTextBox > ( ) . First ( ) ;
2021-11-13 15:58:24 +00:00
2023-06-23 15:59:36 +00:00
return textBox ? . Current . Value = = bank & & string . IsNullOrEmpty ( textBox . PlaceholderText . ToString ( ) ) ;
2021-11-13 15:58:24 +00:00
} ) ;
2021-11-13 16:43:33 +00:00
private void samplePopoverHasIndeterminateBank ( ) = > AddUntilStep ( "sample popover has indeterminate bank" , ( ) = >
2021-11-13 16:07:19 +00:00
{
var popover = this . ChildrenOfType < SamplePointPiece . SampleEditPopover > ( ) . SingleOrDefault ( ) ;
2021-11-13 16:43:33 +00:00
var textBox = popover ? . ChildrenOfType < OsuTextBox > ( ) . First ( ) ;
2021-11-13 16:07:19 +00:00
2021-11-13 16:43:33 +00:00
return textBox ! = null & & string . IsNullOrEmpty ( textBox . Current . Value ) & & ! string . IsNullOrEmpty ( textBox . PlaceholderText . ToString ( ) ) ;
2021-11-13 16:07:19 +00:00
} ) ;
2021-11-13 15:58:24 +00:00
private void dismissPopover ( )
{
2022-05-11 08:25:41 +00:00
AddStep ( "unfocus textbox" , ( ) = > InputManager . Key ( Key . Escape ) ) ;
2021-11-13 15:58:24 +00:00
AddStep ( "dismiss popover" , ( ) = > InputManager . Key ( Key . Escape ) ) ;
2022-05-11 06:51:00 +00:00
AddUntilStep ( "wait for dismiss" , ( ) = > ! this . ChildrenOfType < SamplePointPiece . SampleEditPopover > ( ) . Any ( popover = > popover . IsPresent ) ) ;
2021-11-13 15:58:24 +00:00
}
private void setVolumeViaPopover ( int volume ) = > AddStep ( $"set volume {volume} via popover" , ( ) = >
{
var popover = this . ChildrenOfType < SamplePointPiece . SampleEditPopover > ( ) . Single ( ) ;
2021-11-13 16:21:48 +00:00
var slider = popover . ChildrenOfType < IndeterminateSliderWithTextBoxInput < int > > ( ) . Single ( ) ;
2021-11-13 15:58:24 +00:00
slider . Current . Value = volume ;
} ) ;
private void hitObjectHasSampleVolume ( int objectIndex , int volume ) = > AddAssert ( $"{objectIndex.ToOrdinalWords()} has volume {volume}" , ( ) = >
{
var h = EditorBeatmap . HitObjects . ElementAt ( objectIndex ) ;
2023-04-26 12:21:52 +00:00
return h . Samples . All ( o = > o . Volume = = volume ) ;
2021-11-13 15:58:24 +00:00
} ) ;
private void setBankViaPopover ( string bank ) = > AddStep ( $"set bank {bank} via popover" , ( ) = >
{
var popover = this . ChildrenOfType < SamplePointPiece . SampleEditPopover > ( ) . Single ( ) ;
var textBox = popover . ChildrenOfType < LabelledTextBox > ( ) . First ( ) ;
textBox . Current . Value = bank ;
2021-11-13 17:06:32 +00:00
// force a commit via keyboard.
// this is needed when testing attempting to set empty bank - which should revert to the previous value, but only on commit.
InputManager . ChangeFocus ( textBox ) ;
InputManager . Key ( Key . Enter ) ;
2021-11-13 15:58:24 +00:00
} ) ;
2023-05-16 09:52:35 +00:00
private void hitObjectHasSamples ( int objectIndex , params string [ ] samples ) = > AddAssert ( $"{objectIndex.ToOrdinalWords()} has samples {string.Join(',', samples)}" , ( ) = >
{
var h = EditorBeatmap . HitObjects . ElementAt ( objectIndex ) ;
return h . Samples . Select ( s = > s . Name ) . SequenceEqual ( samples ) ;
} ) ;
2021-11-13 15:58:24 +00:00
private void hitObjectHasSampleBank ( int objectIndex , string bank ) = > AddAssert ( $"{objectIndex.ToOrdinalWords()} has bank {bank}" , ( ) = >
{
var h = EditorBeatmap . HitObjects . ElementAt ( objectIndex ) ;
2023-04-26 12:21:52 +00:00
return h . Samples . All ( o = > o . Bank = = bank ) ;
2021-11-13 15:58:24 +00:00
} ) ;
}
}