2020-12-22 23:33:11 +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.
2022-06-17 15:37:17 +08:00
#nullable disable
2020-12-22 23:33:11 +08:00
using System.Linq ;
using NUnit.Framework ;
2022-04-23 04:19:03 +08:00
using osu.Framework.Extensions.Color4Extensions ;
2020-12-22 23:33:11 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Colour ;
2022-04-23 04:19:03 +08:00
using osu.Framework.Graphics.Containers ;
2020-12-22 23:33:11 +08:00
using osu.Framework.Graphics.Shapes ;
2021-08-20 17:30:49 +08:00
using osu.Framework.Testing ;
2022-04-23 04:19:03 +08:00
using osu.Framework.Utils ;
using osu.Game.Graphics ;
2020-12-22 23:33:11 +08:00
using osu.Game.Graphics.Containers ;
2022-04-23 04:19:03 +08:00
using osu.Game.Graphics.Sprites ;
2020-12-22 23:33:11 +08:00
using osuTK.Graphics ;
2022-04-23 04:19:03 +08:00
using osuTK.Input ;
2020-12-22 23:33:11 +08:00
namespace osu.Game.Tests.Visual.UserInterface
{
public partial class TestSceneSectionsContainer : OsuManualInputManagerTestScene
{
2022-04-23 04:19:03 +08:00
private SectionsContainer < TestSection > container ;
2020-12-22 23:33:11 +08:00
private float custom ;
2022-04-23 04:19:03 +08:00
private const float header_expandable_height = 300 ;
private const float header_fixed_height = 100 ;
[SetUpSteps]
public void SetUpSteps ( )
2020-12-22 23:33:11 +08:00
{
2022-04-23 04:19:03 +08:00
AddStep ( "setup container" , ( ) = >
2020-12-22 23:33:11 +08:00
{
2022-04-23 04:19:03 +08:00
container = new SectionsContainer < TestSection >
2020-12-22 23:33:11 +08:00
{
2022-04-23 04:19:03 +08:00
RelativeSizeAxes = Axes . Y ,
2020-12-22 23:33:11 +08:00
Width = 300 ,
2022-04-23 04:19:03 +08:00
Origin = Anchor . Centre ,
Anchor = Anchor . Centre ,
} ;
container . SelectedSection . ValueChanged + = section = >
{
if ( section . OldValue ! = null )
section . OldValue . Selected = false ;
if ( section . NewValue ! = null )
section . NewValue . Selected = true ;
} ;
Child = container ;
} ) ;
AddToggleStep ( "disable expandable header" , v = > container . ExpandableHeader = v
? null
: new TestBox ( @"Expandable Header" )
{
RelativeSizeAxes = Axes . X ,
Height = header_expandable_height ,
BackgroundColour = new OsuColour ( ) . GreySky ,
} ) ;
AddToggleStep ( "disable fixed header" , v = > container . FixedHeader = v
? null
: new TestBox ( @"Fixed Header" )
{
RelativeSizeAxes = Axes . X ,
Height = header_fixed_height ,
BackgroundColour = new OsuColour ( ) . Red . Opacity ( 0.5f ) ,
} ) ;
AddToggleStep ( "disable footer" , v = > container . Footer = v
? null
: new TestBox ( "Footer" )
{
RelativeSizeAxes = Axes . X ,
Height = 200 ,
BackgroundColour = new OsuColour ( ) . Green4 ,
} ) ;
2020-12-22 23:33:11 +08:00
}
2023-06-07 14:48:04 +08:00
[Test]
public void TestCorrectScrollToWhenContentLoads ( )
{
AddRepeatStep ( "add many sections" , ( ) = > append ( 1f ) , 3 ) ;
AddStep ( "add section with delayed load content" , ( ) = >
{
container . Add ( new TestDelayedLoadSection ( "delayed" ) ) ;
} ) ;
AddStep ( "add final section" , ( ) = > append ( 0.5f ) ) ;
AddStep ( "scroll to final section" , ( ) = > container . ScrollTo ( container . Children . Last ( ) ) ) ;
AddUntilStep ( "correct section selected" , ( ) = > container . SelectedSection . Value = = container . Children . Last ( ) ) ;
AddUntilStep ( "wait for scroll to section" , ( ) = > container . ScreenSpaceDrawQuad . AABBFloat . Contains ( container . Children . Last ( ) . ScreenSpaceDrawQuad . AABBFloat ) ) ;
}
2020-12-22 23:33:11 +08:00
[Test]
public void TestSelection ( )
{
AddStep ( "clear" , ( ) = > container . Clear ( ) ) ;
AddStep ( "add 1/8th" , ( ) = > append ( 1 / 8.0f ) ) ;
AddStep ( "add third" , ( ) = > append ( 1 / 3.0f ) ) ;
AddStep ( "add half" , ( ) = > append ( 1 / 2.0f ) ) ;
AddStep ( "add full" , ( ) = > append ( 1 ) ) ;
AddSliderStep ( "set custom" , 0.1f , 1.1f , 0.5f , i = > custom = i ) ;
AddStep ( "add custom" , ( ) = > append ( custom ) ) ;
AddStep ( "scroll to previous" , ( ) = > container . ScrollTo (
container . Children . Reverse ( ) . SkipWhile ( s = > s ! = container . SelectedSection . Value ) . Skip ( 1 ) . FirstOrDefault ( ) ? ? container . Children . First ( )
) ) ;
AddStep ( "scroll to next" , ( ) = > container . ScrollTo (
container . Children . SkipWhile ( s = > s ! = container . SelectedSection . Value ) . Skip ( 1 ) . FirstOrDefault ( ) ? ? container . Children . Last ( )
) ) ;
AddStep ( "scroll up" , ( ) = > triggerUserScroll ( 1 ) ) ;
AddStep ( "scroll down" , ( ) = > triggerUserScroll ( - 1 ) ) ;
2021-08-20 17:30:49 +08:00
AddStep ( "scroll up a bit" , ( ) = > triggerUserScroll ( 0.1f ) ) ;
AddStep ( "scroll down a bit" , ( ) = > triggerUserScroll ( - 0.1f ) ) ;
2020-12-22 23:33:11 +08:00
}
[Test]
2021-08-20 17:30:49 +08:00
public void TestCorrectSelectionAndVisibleTop ( )
2020-12-22 23:33:11 +08:00
{
const int sections_count = 11 ;
float [ ] alternating = { 0.07f , 0.33f , 0.16f , 0.33f } ;
AddStep ( "fill with sections" , ( ) = >
{
for ( int i = 0 ; i < sections_count ; i + + )
append ( alternating [ i % alternating . Length ] ) ;
} ) ;
void step ( int scrollIndex )
{
AddStep ( $"scroll to section {scrollIndex + 1}" , ( ) = > container . ScrollTo ( container . Children [ scrollIndex ] ) ) ;
AddUntilStep ( "correct section selected" , ( ) = > container . SelectedSection . Value = = container . Children [ scrollIndex ] ) ;
2021-08-20 17:30:49 +08:00
AddUntilStep ( "section top is visible" , ( ) = >
{
2022-04-23 04:19:03 +08:00
var scrollContainer = container . ChildrenOfType < UserTrackingScrollContainer > ( ) . Single ( ) ;
float sectionPosition = scrollContainer . GetChildPosInContent ( container . Children [ scrollIndex ] ) ;
return scrollContainer . Current < sectionPosition ;
2021-08-20 17:30:49 +08:00
} ) ;
2020-12-22 23:33:11 +08:00
}
for ( int i = 1 ; i < sections_count ; i + + )
step ( i ) ;
for ( int i = sections_count - 2 ; i > = 0 ; i - - )
step ( i ) ;
AddStep ( "scroll almost to end" , ( ) = > container . ScrollTo ( container . Children [ sections_count - 2 ] ) ) ;
AddUntilStep ( "correct section selected" , ( ) = > container . SelectedSection . Value = = container . Children [ sections_count - 2 ] ) ;
AddStep ( "scroll down" , ( ) = > triggerUserScroll ( - 1 ) ) ;
AddUntilStep ( "correct section selected" , ( ) = > container . SelectedSection . Value = = container . Children [ sections_count - 1 ] ) ;
}
2022-04-24 06:09:24 +08:00
[Test]
public void TestNavigation ( )
{
AddRepeatStep ( "add sections" , ( ) = > append ( 1f ) , 3 ) ;
AddUntilStep ( "wait for load" , ( ) = > container . Children . Any ( ) ) ;
AddStep ( "hover sections container" , ( ) = > InputManager . MoveMouseTo ( container ) ) ;
AddStep ( "press page down" , ( ) = > InputManager . Key ( Key . PageDown ) ) ;
AddUntilStep ( "scrolled one page down" , ( ) = >
{
var scroll = container . ChildrenOfType < UserTrackingScrollContainer > ( ) . First ( ) ;
return Precision . AlmostEquals ( scroll . Current , Content . DrawHeight - header_fixed_height , 1f ) ;
} ) ;
AddStep ( "press page down" , ( ) = > InputManager . Key ( Key . PageDown ) ) ;
AddUntilStep ( "scrolled two pages down" , ( ) = >
{
var scroll = container . ChildrenOfType < UserTrackingScrollContainer > ( ) . First ( ) ;
return Precision . AlmostEquals ( scroll . Current , ( Content . DrawHeight - header_fixed_height ) * 2 , 1f ) ;
} ) ;
AddStep ( "press page up" , ( ) = > InputManager . Key ( Key . PageUp ) ) ;
AddUntilStep ( "scrolled one page up" , ( ) = >
{
var scroll = container . ChildrenOfType < UserTrackingScrollContainer > ( ) . First ( ) ;
return Precision . AlmostEquals ( scroll . Current , Content . DrawHeight - header_fixed_height , 1f ) ;
} ) ;
}
2022-04-23 04:19:03 +08:00
private static readonly ColourInfo selected_colour = ColourInfo . GradientVertical ( new OsuColour ( ) . Orange2 , new OsuColour ( ) . Orange3 ) ;
2020-12-22 23:33:11 +08:00
private static readonly ColourInfo default_colour = ColourInfo . GradientVertical ( Color4 . White , Color4 . DarkGray ) ;
private void append ( float multiplier )
{
2022-04-23 04:19:03 +08:00
float fixedHeaderHeight = container . FixedHeader ? . Height ? ? 0 ;
float expandableHeaderHeight = container . ExpandableHeader ? . Height ? ? 0 ;
float totalHeaderHeight = expandableHeaderHeight + fixedHeaderHeight ;
float effectiveHeaderHeight = totalHeaderHeight ;
// if we're in the "next page" of the sections container,
// height of the expandable header should not be accounted.
var scrollContent = container . ChildrenOfType < UserTrackingScrollContainer > ( ) . Single ( ) . ScrollContent ;
if ( totalHeaderHeight + scrollContent . Height > = Content . DrawHeight )
effectiveHeaderHeight - = expandableHeaderHeight ;
container . Add ( new TestSection ( $"Section #{container.Children.Count + 1}" )
2020-12-22 23:33:11 +08:00
{
Width = 300 ,
2022-04-23 04:19:03 +08:00
Height = ( Content . DrawHeight - effectiveHeaderHeight ) * multiplier ,
2020-12-22 23:33:11 +08:00
Colour = default_colour
} ) ;
}
private void triggerUserScroll ( float direction )
{
InputManager . MoveMouseTo ( container ) ;
InputManager . ScrollVerticalBy ( direction ) ;
}
2023-06-07 14:48:04 +08:00
private partial class TestDelayedLoadSection : TestSection
{
public TestDelayedLoadSection ( string label )
: base ( label )
{
BackgroundColour = default_colour ;
Width = 300 ;
AutoSizeAxes = Axes . Y ;
}
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
Box box ;
Add ( box = new Box
{
Alpha = 0.01f ,
RelativeSizeAxes = Axes . X ,
} ) ;
// Emulate an operation that will be inhibited by IsMaskedAway.
box . ResizeHeightTo ( 2000 , 50 ) ;
}
}
2022-04-23 04:19:03 +08:00
private partial class TestSection : TestBox
2020-12-22 23:33:11 +08:00
{
public bool Selected
{
2022-04-23 04:19:03 +08:00
set = > BackgroundColour = value ? selected_colour : default_colour ;
}
public TestSection ( string label )
: base ( label )
{
BackgroundColour = default_colour ;
}
}
private partial class TestBox : Container
{
private readonly Box background ;
private readonly OsuSpriteText text ;
public ColourInfo BackgroundColour
{
set
{
background . Colour = value ;
text . Colour = OsuColour . ForegroundTextColourFor ( value . AverageColour ) ;
}
}
public TestBox ( string label )
{
Children = new Drawable [ ]
{
background = new Box
{
RelativeSizeAxes = Axes . Both ,
} ,
text = new OsuSpriteText
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Text = label ,
Font = OsuFont . Default . With ( size : 36 ) ,
}
} ;
2020-12-22 23:33:11 +08:00
}
}
}
}