Merge branch 'master' into page-selector

This commit is contained in:
Dean Herbert
2022-01-04 16:44:22 +09:00
committed by GitHub
3752 changed files with 223085 additions and 51129 deletions

View File

@ -1,8 +1,6 @@
// 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;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -14,11 +12,6 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneBackButton : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(TwoLayerButton)
};
public TestSceneBackButton()
{
BackButton button;

View File

@ -5,18 +5,19 @@ using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Play;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
@ -24,46 +25,160 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestSceneBeatSyncedContainer : OsuTestScene
{
private readonly NowPlayingOverlay np;
private TestBeatSyncedContainer beatContainer;
public override IReadOnlyList<Type> RequiredTypes => new[]
private MasterGameplayClockContainer gameplayClockContainer;
[SetUpSteps]
public void SetUpSteps()
{
typeof(BeatSyncedContainer)
};
[Cached]
private MusicController musicController = new MusicController();
public TestSceneBeatSyncedContainer()
{
Clock = new FramedClock();
Clock.ProcessFrame();
AddRange(new Drawable[]
AddStep("Set beatmap", () =>
{
musicController,
new BeatContainer
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
},
np = new NowPlayingOverlay
{
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
}
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
});
AddStep("Create beat sync container", () =>
{
Children = new Drawable[]
{
gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, 0)
{
Child = beatContainer = new TestBeatSyncedContainer
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
},
}
};
});
AddStep("Start playback", () => gameplayClockContainer.Start());
}
protected override void LoadComplete()
[TestCase(false)]
[TestCase(true)]
public void TestDisallowMistimedEventFiring(bool allowMistimed)
{
base.LoadComplete();
np.ToggleVisibility();
int? lastBeatIndex = null;
double? lastActuationTime = null;
TimingControlPoint lastTimingPoint = null;
AddStep($"set mistimed to {(allowMistimed ? "allowed" : "disallowed")}", () => beatContainer.AllowMistimedEventFiring = allowMistimed);
AddStep("Set time before zero", () =>
{
beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) =>
{
lastActuationTime = gameplayClockContainer.CurrentTime;
lastTimingPoint = timingControlPoint;
lastBeatIndex = i;
beatContainer.NewBeat = null;
};
gameplayClockContainer.Seek(-1000);
});
AddUntilStep("wait for trigger", () => lastBeatIndex != null);
if (!allowMistimed)
{
AddAssert("trigger is near beat length", () => lastActuationTime != null && lastBeatIndex != null && Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, BeatSyncedContainer.MISTIMED_ALLOWANCE));
}
else
{
AddAssert("trigger is not near beat length", () => lastActuationTime != null && lastBeatIndex != null && !Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value, BeatSyncedContainer.MISTIMED_ALLOWANCE));
}
}
private class BeatContainer : BeatSyncedContainer
[Test]
public void TestNegativeBeatsStillUsingBeatmapTiming()
{
private const int flash_layer_heigth = 150;
int? lastBeatIndex = null;
double? lastBpm = null;
AddStep("Set time before zero", () =>
{
beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) =>
{
lastBeatIndex = i;
lastBpm = timingControlPoint.BPM;
};
gameplayClockContainer.Seek(-1000);
});
AddUntilStep("wait for trigger", () => lastBpm != null);
AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 128));
AddAssert("beat index is less than zero", () => lastBeatIndex < 0);
}
[Test]
public void TestIdleBeatOnPausedClock()
{
double? lastBpm = null;
AddStep("bind event", () =>
{
beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) => lastBpm = timingControlPoint.BPM;
});
AddUntilStep("wait for trigger", () => lastBpm != null);
AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 128));
AddStep("pause gameplay clock", () =>
{
lastBpm = null;
gameplayClockContainer.Stop();
});
AddUntilStep("wait for trigger", () => lastBpm != null);
AddAssert("bpm is default", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60));
}
[TestCase(true)]
[TestCase(false)]
public void TestEarlyActivationEffectPoint(bool earlyActivating)
{
double earlyActivationMilliseconds = earlyActivating ? 100 : 0;
ControlPoint actualEffectPoint = null;
AddStep($"set early activation to {earlyActivationMilliseconds}", () => beatContainer.EarlyActivationMilliseconds = earlyActivationMilliseconds);
AddStep("seek before kiai effect point", () =>
{
ControlPoint expectedEffectPoint = Beatmap.Value.Beatmap.ControlPointInfo.EffectPoints.First(ep => ep.KiaiMode);
actualEffectPoint = null;
beatContainer.AllowMistimedEventFiring = false;
beatContainer.NewBeat = (i, timingControlPoint, effectControlPoint, channelAmplitudes) =>
{
if (Precision.AlmostEquals(gameplayClockContainer.CurrentTime + earlyActivationMilliseconds, expectedEffectPoint.Time, BeatSyncedContainer.MISTIMED_ALLOWANCE))
actualEffectPoint = effectControlPoint;
};
gameplayClockContainer.Seek(expectedEffectPoint.Time - earlyActivationMilliseconds);
});
AddUntilStep("wait for effect point", () => actualEffectPoint != null);
AddAssert("effect has kiai", () => actualEffectPoint != null && ((EffectControlPoint)actualEffectPoint).KiaiMode);
}
private class TestBeatSyncedContainer : BeatSyncedContainer
{
private const int flash_layer_height = 150;
public new bool AllowMistimedEventFiring
{
get => base.AllowMistimedEventFiring;
set => base.AllowMistimedEventFiring = value;
}
public new double EarlyActivationMilliseconds
{
get => base.EarlyActivationMilliseconds;
set => base.EarlyActivationMilliseconds = value;
}
private readonly InfoString timingPointCount;
private readonly InfoString currentTimingPoint;
@ -73,10 +188,11 @@ namespace osu.Game.Tests.Visual.UserInterface
private readonly InfoString adjustedBeatLength;
private readonly InfoString timeUntilNextBeat;
private readonly InfoString timeSinceLastBeat;
private readonly InfoString currentTime;
private readonly Box flashLayer;
public BeatContainer()
public TestBeatSyncedContainer()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
@ -88,7 +204,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Bottom = flash_layer_heigth },
Margin = new MarginPadding { Bottom = flash_layer_height },
Children = new Drawable[]
{
new Box
@ -104,6 +220,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
currentTime = new InfoString(@"Current time"),
timingPointCount = new InfoString(@"Timing points amount"),
currentTimingPoint = new InfoString(@"Current timing point"),
beatCount = new InfoString(@"Beats amount (in the current timing point)"),
@ -122,7 +239,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = flash_layer_heigth,
Height = flash_layer_height,
Children = new Drawable[]
{
new Box
@ -139,8 +256,13 @@ namespace osu.Game.Tests.Visual.UserInterface
}
}
};
}
Beatmap.ValueChanged += delegate
protected override void LoadComplete()
{
base.LoadComplete();
Beatmap.BindValueChanged(_ =>
{
timingPointCount.Value = 0;
currentTimingPoint.Value = 0;
@ -150,7 +272,7 @@ namespace osu.Game.Tests.Visual.UserInterface
adjustedBeatLength.Value = 0;
timeUntilNextBeat.Value = 0;
timeSinceLastBeat.Value = 0;
};
}, true);
}
private List<TimingControlPoint> timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ToList();
@ -170,7 +292,7 @@ namespace osu.Game.Tests.Visual.UserInterface
if (timingPoints.Count == 0) return 0;
if (timingPoints[^1] == current)
return (int)Math.Ceiling((Beatmap.Value.Track.Length - current.Time) / current.BeatLength);
return (int)Math.Ceiling((BeatSyncClock.CurrentTime - current.Time) / current.BeatLength);
return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength);
}
@ -180,9 +302,12 @@ namespace osu.Game.Tests.Visual.UserInterface
base.Update();
timeUntilNextBeat.Value = TimeUntilNextBeat;
timeSinceLastBeat.Value = TimeSinceLastBeat;
currentTime.Value = BeatSyncClock.CurrentTime;
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
public Action<int, TimingControlPoint, EffectControlPoint, ChannelAmplitudes> NewBeat;
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
@ -193,7 +318,9 @@ namespace osu.Game.Tests.Visual.UserInterface
beatsPerMinute.Value = 60000 / timingPoint.BeatLength;
adjustedBeatLength.Value = timingPoint.BeatLength;
flashLayer.FadeOutFromOne(timingPoint.BeatLength);
flashLayer.FadeOutFromOne(timingPoint.BeatLength / 4);
NewBeat?.Invoke(beatIndex, timingPoint, effectPoint, amplitudes);
}
}
@ -206,7 +333,7 @@ namespace osu.Game.Tests.Visual.UserInterface
public double Value
{
set => valueText.Text = $"{value:G}";
set => valueText.Text = $"{value:0.##}";
}
public InfoString(string header)

View File

@ -0,0 +1,55 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps.Drawables.Cards;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapListing;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneBeatmapListingCardSizeTabControl : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private readonly Bindable<BeatmapCardSize> cardSize = new Bindable<BeatmapCardSize>();
private SpriteText cardSizeText;
[BackgroundDependencyLoader]
private void load()
{
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
cardSizeText = new OsuSpriteText
{
Font = OsuFont.Default.With(size: 24)
},
new BeatmapListingCardSizeTabControl
{
Current = cardSize,
Anchor = Anchor.Centre,
Origin = Anchor.Centre
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
cardSize.BindValueChanged(size => cardSizeText.Text = $"Current size: {size.NewValue}", true);
}
}
}

View File

@ -0,0 +1,129 @@
// 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;
using Humanizer;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapListing;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneBeatmapListingSearchControl : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private BeatmapListingSearchControl control;
private OsuConfigManager localConfig;
[BackgroundDependencyLoader]
private void load()
{
Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage));
}
[SetUp]
public void SetUp() => Schedule(() =>
{
OsuSpriteText query;
OsuSpriteText general;
OsuSpriteText ruleset;
OsuSpriteText category;
OsuSpriteText genre;
OsuSpriteText language;
OsuSpriteText extra;
OsuSpriteText ranks;
OsuSpriteText played;
OsuSpriteText explicitMap;
Children = new Drawable[]
{
control = new BeatmapListingSearchControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
query = new OsuSpriteText(),
general = new OsuSpriteText(),
ruleset = new OsuSpriteText(),
category = new OsuSpriteText(),
genre = new OsuSpriteText(),
language = new OsuSpriteText(),
extra = new OsuSpriteText(),
ranks = new OsuSpriteText(),
played = new OsuSpriteText(),
explicitMap = new OsuSpriteText(),
}
}
};
control.Query.BindValueChanged(q => query.Text = $"Query: {q.NewValue}", true);
control.General.BindCollectionChanged((u, v) => general.Text = $"General: {(control.General.Any() ? string.Join('.', control.General.Select(i => i.ToString().Underscore())) : "")}", true);
control.Ruleset.BindValueChanged(r => ruleset.Text = $"Ruleset: {r.NewValue}", true);
control.Category.BindValueChanged(c => category.Text = $"Category: {c.NewValue}", true);
control.Genre.BindValueChanged(g => genre.Text = $"Genre: {g.NewValue}", true);
control.Language.BindValueChanged(l => language.Text = $"Language: {l.NewValue}", true);
control.Extra.BindCollectionChanged((u, v) => extra.Text = $"Extra: {(control.Extra.Any() ? string.Join('.', control.Extra.Select(i => i.ToString().ToLowerInvariant())) : "")}", true);
control.Ranks.BindCollectionChanged((u, v) => ranks.Text = $"Ranks: {(control.Ranks.Any() ? string.Join('.', control.Ranks.Select(i => i.ToString())) : "")}", true);
control.Played.BindValueChanged(p => played.Text = $"Played: {p.NewValue}", true);
control.ExplicitContent.BindValueChanged(e => explicitMap.Text = $"Explicit Maps: {e.NewValue}", true);
});
[Test]
public void TestCovers()
{
AddStep("Set beatmap", () => control.BeatmapSet = beatmap_set);
AddStep("Set beatmap (no cover)", () => control.BeatmapSet = no_cover_beatmap_set);
AddStep("Set null beatmap", () => control.BeatmapSet = null);
}
[Test]
public void TestExplicitConfig()
{
AddStep("configure explicit content to allowed", () => localConfig.SetValue(OsuSetting.ShowOnlineExplicitContent, true));
AddAssert("explicit control set to show", () => control.ExplicitContent.Value == SearchExplicit.Show);
AddStep("configure explicit content to disallowed", () => localConfig.SetValue(OsuSetting.ShowOnlineExplicitContent, false));
AddAssert("explicit control set to hide", () => control.ExplicitContent.Value == SearchExplicit.Hide);
}
protected override void Dispose(bool isDisposing)
{
localConfig?.Dispose();
base.Dispose(isDisposing);
}
private static readonly APIBeatmapSet beatmap_set = new APIBeatmapSet
{
Covers = new BeatmapSetOnlineCovers
{
Cover = "https://assets.ppy.sh/beatmaps/1094296/covers/cover@2x.jpg?1581416305"
}
};
private static readonly APIBeatmapSet no_cover_beatmap_set = new APIBeatmapSet
{
Covers = new BeatmapSetOnlineCovers
{
Cover = string.Empty
}
};
}
}

View File

@ -0,0 +1,47 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapListing;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneBeatmapListingSortTabControl : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
public TestSceneBeatmapListingSortTabControl()
{
BeatmapListingSortTabControl control;
OsuSpriteText current;
OsuSpriteText direction;
Add(control = new BeatmapListingSortTabControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
Add(new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
current = new OsuSpriteText(),
direction = new OsuSpriteText()
}
});
control.SortDirection.BindValueChanged(sortDirection => direction.Text = $"Sort direction: {sortDirection.NewValue}", true);
control.Current.BindValueChanged(criteria => current.Text = $"Criteria: {criteria.NewValue}", true);
}
}
}

View File

@ -0,0 +1,48 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapListing;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneBeatmapSearchFilter : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private readonly ReverseChildIDFillFlowContainer<Drawable> resizableContainer;
public TestSceneBeatmapSearchFilter()
{
Add(resizableContainer = new ReverseChildIDFillFlowContainer<Drawable>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
new BeatmapSearchRulesetFilterRow(),
new BeatmapSearchFilterRow<SearchCategory>("Categories"),
new BeatmapSearchFilterRow<SearchCategory>("Header Name")
}
});
}
[Test]
public void TestResize()
{
AddStep("Resize to 0.3", () => resizableContainer.ResizeWidthTo(0.3f, 1000));
AddStep("Resize to 1", () => resizableContainer.ResizeWidthTo(1, 1000));
}
}
}

View File

@ -1,9 +1,11 @@
// 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;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
@ -12,11 +14,11 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestSceneBreadcrumbControl : OsuTestScene
{
private readonly BreadcrumbControl<BreadcrumbTab> breadcrumbs;
private readonly TestBreadcrumbControl breadcrumbs;
public TestSceneBreadcrumbControl()
{
Add(breadcrumbs = new BreadcrumbControl<BreadcrumbTab>
Add(breadcrumbs = new TestBreadcrumbControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -25,8 +27,13 @@ namespace osu.Game.Tests.Visual.UserInterface
});
AddStep(@"first", () => breadcrumbs.Current.Value = BreadcrumbTab.Click);
assertVisible(1);
AddStep(@"second", () => breadcrumbs.Current.Value = BreadcrumbTab.The);
assertVisible(2);
AddStep(@"third", () => breadcrumbs.Current.Value = BreadcrumbTab.Circles);
assertVisible(3);
}
[BackgroundDependencyLoader]
@ -35,11 +42,27 @@ namespace osu.Game.Tests.Visual.UserInterface
breadcrumbs.StripColour = colours.Blue;
}
private void assertVisible(int count) => AddAssert($"first {count} item(s) visible", () =>
{
for (int i = 0; i < count; i++)
{
if (breadcrumbs.GetDrawable((BreadcrumbTab)i).State != Visibility.Visible)
return false;
}
return true;
});
private enum BreadcrumbTab
{
Click,
The,
Circles,
}
private class TestBreadcrumbControl : BreadcrumbControl<BreadcrumbTab>
{
public BreadcrumbTabItem GetDrawable(BreadcrumbTab tab) => (BreadcrumbTabItem)TabContainer.First(t => t.Value == tab);
}
}
}

View File

@ -0,0 +1,86 @@
// 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;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneBreadcrumbControlHeader : OsuTestScene
{
private static readonly string[] items = { "first", "second", "third", "fourth", "fifth" };
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Red);
private TestHeader header;
[SetUp]
public void SetUp() => Schedule(() =>
{
Child = header = new TestHeader
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
});
[Test]
public void TestAddAndRemoveItem()
{
foreach (string item in items.Skip(1))
AddStep($"Add {item} item", () => header.AddItem(item));
foreach (string item in items.Reverse().SkipLast(3))
AddStep($"Remove {item} item", () => header.RemoveItem(item));
AddStep("Clear items", () => header.ClearItems());
foreach (string item in items)
AddStep($"Add {item} item", () => header.AddItem(item));
foreach (string item in items)
AddStep($"Remove {item} item", () => header.RemoveItem(item));
}
private class TestHeader : BreadcrumbControlOverlayHeader
{
public TestHeader()
{
TabControl.AddItem(items[0]);
Current.Value = items[0];
}
public void AddItem(string value)
{
TabControl.AddItem(value);
Current.Value = TabControl.Items.LastOrDefault();
}
public void RemoveItem(string value)
{
TabControl.RemoveItem(value);
Current.Value = TabControl.Items.LastOrDefault();
}
public void ClearItems()
{
TabControl.Clear();
Current.Value = null;
}
protected override OverlayTitle CreateTitle() => new TestTitle();
}
private class TestTitle : OverlayTitle
{
public TestTitle()
{
Title = "Test Title";
}
}
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
@ -17,13 +16,6 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestSceneButtonSystem : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ButtonSystem),
typeof(ButtonArea),
typeof(Button)
};
private OsuLogo logo;
private ButtonSystem buttons;

View File

@ -0,0 +1,91 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneColourPicker : OsuTestScene
{
private readonly Bindable<Colour4> colour = new Bindable<Colour4>(Colour4.Aquamarine);
[SetUpSteps]
public void SetUpSteps()
{
AddStep("create pickers", () => Child = new GridContainer
{
RelativeSizeAxes = Axes.Both,
ColumnDimensions = new[]
{
new Dimension(),
new Dimension()
},
Content = new[]
{
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = @"No OverlayColourProvider",
Font = OsuFont.Default.With(size: 40)
},
new OsuColourPicker
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Current = { BindTarget = colour },
}
}
},
new ColourProvidingContainer(OverlayColourScheme.Blue)
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = @"With blue OverlayColourProvider",
Font = OsuFont.Default.With(size: 40)
},
new OsuColourPicker
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Current = { BindTarget = colour },
}
}
}
}
}
});
AddStep("set green", () => colour.Value = Colour4.LimeGreen);
AddStep("set white", () => colour.Value = Colour4.White);
AddStep("set red", () => colour.Value = Colour4.Red);
}
private class ColourProvidingContainer : Container
{
[Cached]
private OverlayColourProvider provider { get; }
public ColourProvidingContainer(OverlayColourScheme colourScheme)
{
provider = new OverlayColourProvider(colourScheme);
}
}
}
}

View File

@ -0,0 +1,143 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Overlays.Comments;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneCommentEditor : OsuManualInputManagerTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private TestCommentEditor commentEditor;
private TestCancellableCommentEditor cancellableCommentEditor;
[SetUp]
public void SetUp() => Schedule(() =>
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Y,
Width = 800,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 20),
Children = new Drawable[]
{
commentEditor = new TestCommentEditor(),
cancellableCommentEditor = new TestCancellableCommentEditor()
}
}));
[Test]
public void TestCommitViaKeyboard()
{
AddStep("click on text box", () =>
{
InputManager.MoveMouseTo(commentEditor);
InputManager.Click(MouseButton.Left);
});
AddStep("enter text", () => commentEditor.Current.Value = "text");
AddStep("press Enter", () => InputManager.Key(Key.Enter));
AddAssert("text committed", () => commentEditor.CommittedText == "text");
AddAssert("button is loading", () => commentEditor.IsLoading);
}
[Test]
public void TestCommitViaKeyboardWhenEmpty()
{
AddStep("click on text box", () =>
{
InputManager.MoveMouseTo(commentEditor);
InputManager.Click(MouseButton.Left);
});
AddStep("press Enter", () => InputManager.Key(Key.Enter));
AddAssert("no text committed", () => commentEditor.CommittedText == null);
AddAssert("button is not loading", () => !commentEditor.IsLoading);
}
[Test]
public void TestCommitViaButton()
{
AddStep("click on text box", () =>
{
InputManager.MoveMouseTo(commentEditor);
InputManager.Click(MouseButton.Left);
});
AddStep("enter text", () => commentEditor.Current.Value = "some other text");
AddStep("click submit", () =>
{
InputManager.MoveMouseTo(commentEditor.ButtonsContainer);
InputManager.Click(MouseButton.Left);
});
AddAssert("text committed", () => commentEditor.CommittedText == "some other text");
AddAssert("button is loading", () => commentEditor.IsLoading);
}
[Test]
public void TestCancelAction()
{
AddStep("click cancel button", () =>
{
InputManager.MoveMouseTo(cancellableCommentEditor.ButtonsContainer);
InputManager.Click(MouseButton.Left);
});
AddAssert("cancel action fired", () => cancellableCommentEditor.Cancelled);
}
private class TestCommentEditor : CommentEditor
{
public new Bindable<string> Current => base.Current;
public new FillFlowContainer ButtonsContainer => base.ButtonsContainer;
public string CommittedText { get; private set; }
public TestCommentEditor()
{
OnCommit = onCommit;
}
private void onCommit(string value)
{
CommittedText = value;
Scheduler.AddDelayed(() => IsLoading = false, 1000);
}
protected override string FooterText => @"Footer text. And it is pretty long. Cool.";
protected override string CommitButtonText => @"Commit";
protected override string TextBoxPlaceholder => @"This text box is empty";
}
private class TestCancellableCommentEditor : CancellableCommentEditor
{
public new FillFlowContainer ButtonsContainer => base.ButtonsContainer;
protected override string FooterText => @"Wow, another one. Sicc";
public bool Cancelled { get; private set; }
public TestCancellableCommentEditor()
{
OnCancel = () => Cancelled = true;
}
protected override string CommitButtonText => @"Save";
protected override string TextBoxPlaceholder => @"Multiline textboxes soon";
}
}
}

View File

@ -0,0 +1,67 @@
// 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 osu.Game.Overlays.Comments.Buttons;
using osu.Framework.Graphics;
using osu.Framework.Allocation;
using osu.Game.Overlays;
using osu.Framework.Graphics.Containers;
using osuTK;
using NUnit.Framework;
using System.Linq;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Testing;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneCommentRepliesButton : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private readonly TestButton button;
public TestSceneCommentRepliesButton()
{
Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
button = new TestButton(),
new LoadRepliesButton
{
Action = () => { }
},
new ShowRepliesButton(1),
new ShowRepliesButton(2)
}
};
}
[Test]
public void TestArrowDirection()
{
AddStep("Set upwards", () => button.SetIconDirection(true));
AddAssert("Icon facing upwards", () => button.Icon.Scale.Y == -1);
AddStep("Set downwards", () => button.SetIconDirection(false));
AddAssert("Icon facing downwards", () => button.Icon.Scale.Y == 1);
}
private class TestButton : CommentRepliesButton
{
public SpriteIcon Icon => this.ChildrenOfType<SpriteIcon>().First();
public TestButton()
{
Text = "sample text";
}
public new void SetIconDirection(bool upwards) => base.SetIconDirection(upwards);
}
}
}

View File

@ -1,6 +1,8 @@
// 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.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -68,13 +70,40 @@ namespace osu.Game.Tests.Visual.UserInterface
);
}
private class MyContextMenuContainer : Container, IHasContextMenu
private static MenuItem[] makeMenu()
{
public MenuItem[] ContextMenuItems => new MenuItem[]
return new MenuItem[]
{
new OsuMenuItem(@"Some option"),
new OsuMenuItem(@"Highlighted option", MenuItemType.Highlighted),
new OsuMenuItem(@"Another option"),
new OsuMenuItem(@"Nested option >")
{
Items = new MenuItem[]
{
new OsuMenuItem(@"Sub-One"),
new OsuMenuItem(@"Sub-Two"),
new OsuMenuItem(@"Sub-Three"),
new OsuMenuItem(@"Sub-Nested option >")
{
Items = new MenuItem[]
{
new OsuMenuItem(@"Double Sub-One"),
new OsuMenuItem(@"Double Sub-Two"),
new OsuMenuItem(@"Double Sub-Three"),
new OsuMenuItem(@"Sub-Sub-Nested option >")
{
Items = new MenuItem[]
{
new OsuMenuItem(@"Too Deep One"),
new OsuMenuItem(@"Too Deep Two"),
new OsuMenuItem(@"Too Deep Three"),
}
}
}
}
}
},
new OsuMenuItem(@"Choose me please"),
new OsuMenuItem(@"And me too"),
new OsuMenuItem(@"Trying to fill"),
@ -82,17 +111,29 @@ namespace osu.Game.Tests.Visual.UserInterface
};
}
private class MyContextMenuContainer : Container, IHasContextMenu
{
public MenuItem[] ContextMenuItems => makeMenu();
}
private class AnotherContextMenuContainer : Container, IHasContextMenu
{
public MenuItem[] ContextMenuItems => new MenuItem[]
public MenuItem[] ContextMenuItems
{
new OsuMenuItem(@"Simple option"),
new OsuMenuItem(@"Simple very very long option"),
new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)),
};
get
{
List<MenuItem> items = makeMenu().ToList();
items.AddRange(new MenuItem[]
{
new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)),
});
return items.ToArray();
}
}
}
}
}

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.Sprites;
using osuTK;
@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneCursors : ManualInputManagerTestScene
public class TestSceneCursors : OsuManualInputManagerTestScene
{
private readonly MenuCursorContainer menuCursorContainer;
private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6];

View File

@ -0,0 +1,127 @@
// 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 osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Game.Overlays.Dashboard.Home;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Framework.Allocation;
using System;
using osu.Framework.Graphics.Shapes;
using System.Collections.Generic;
using osu.Game.Online.API.Requests.Responses;
using APIUser = osu.Game.Online.API.Requests.Responses.APIUser;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneDashboardBeatmapListing : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
private readonly Container content;
public TestSceneDashboardBeatmapListing()
{
Add(content = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Y,
Width = 300,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background4
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = 10 },
Child = new DashboardBeatmapListing(new_beatmaps, popular_beatmaps)
}
}
});
}
protected override void LoadComplete()
{
base.LoadComplete();
AddStep("Set width to 500", () => content.ResizeWidthTo(500, 500));
AddStep("Set width to 300", () => content.ResizeWidthTo(300, 500));
}
private static readonly List<APIBeatmapSet> new_beatmaps = new List<APIBeatmapSet>
{
new APIBeatmapSet
{
Title = "Very Long Title (TV size) [TATOE]",
Artist = "This artist has a really long name how is this possible",
Author = new APIUser
{
Username = "author",
Id = 100
},
Covers = new BeatmapSetOnlineCovers
{
Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608",
},
Ranked = DateTimeOffset.Now
},
new APIBeatmapSet
{
Title = "Very Long Title (TV size) [TATOE]",
Artist = "This artist has a really long name how is this possible",
Author = new APIUser
{
Username = "author",
Id = 100
},
Covers = new BeatmapSetOnlineCovers
{
Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608",
},
Ranked = DateTimeOffset.Now
}
};
private static readonly List<APIBeatmapSet> popular_beatmaps = new List<APIBeatmapSet>
{
new APIBeatmapSet
{
Title = "Very Long Title (TV size) [TATOE]",
Artist = "This artist has a really long name how is this possible",
Author = new APIUser
{
Username = "author",
Id = 100
},
Covers = new BeatmapSetOnlineCovers
{
Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608",
},
Ranked = DateTimeOffset.Now
},
new APIBeatmapSet
{
Title = "Very Long Title (TV size) [TATOE]",
Artist = "This artist has a really long name how is this possible",
Author = new APIUser
{
Username = "author",
Id = 100
},
Covers = new BeatmapSetOnlineCovers
{
Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608",
},
Ranked = DateTimeOffset.Now
}
};
}
}

View File

@ -0,0 +1,177 @@
// 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.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Tests.Resources;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneDeleteLocalScore : OsuManualInputManagerTestScene
{
private readonly ContextMenuContainer contextMenuContainer;
private readonly BeatmapLeaderboard leaderboard;
private RulesetStore rulesetStore;
private BeatmapManager beatmapManager;
private ScoreManager scoreManager;
private readonly List<ScoreInfo> importedScores = new List<ScoreInfo>();
private BeatmapInfo beatmapInfo;
[Cached]
private readonly DialogOverlay dialogOverlay;
public TestSceneDeleteLocalScore()
{
Children = new Drawable[]
{
contextMenuContainer = new OsuContextMenuContainer
{
RelativeSizeAxes = Axes.Both,
Child = leaderboard = new BeatmapLeaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
Scope = BeatmapLeaderboardScope.Local,
BeatmapInfo = new BeatmapInfo
{
ID = 1,
Metadata = new BeatmapMetadata
{
ID = 1,
Title = "TestSong",
Artist = "TestArtist",
Author = new APIUser
{
Username = "TestAuthor"
},
},
DifficultyName = "Insane"
},
}
},
dialogOverlay = new DialogOverlay()
};
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory));
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, ContextFactory, Scheduler));
beatmapInfo = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Value.Beatmaps[0];
for (int i = 0; i < 50; i++)
{
var score = new ScoreInfo
{
OnlineID = i,
BeatmapInfo = beatmapInfo,
BeatmapInfoID = beatmapInfo.ID,
Accuracy = RNG.NextDouble(),
TotalScore = RNG.Next(1, 1000000),
MaxCombo = RNG.Next(1, 1000),
Rank = ScoreRank.XH,
User = new APIUser { Username = "TestUser" },
};
importedScores.Add(scoreManager.Import(score).Result.Value);
}
return dependencies;
}
[SetUp]
public void Setup() => Schedule(() =>
{
// Due to soft deletions, we can re-use deleted scores between test runs
scoreManager.Undelete(scoreManager.QueryScores(s => s.DeletePending).ToList());
leaderboard.Scores = null;
leaderboard.FinishTransforms(true); // After setting scores, we may be waiting for transforms to expire drawables
leaderboard.BeatmapInfo = beatmapInfo;
leaderboard.RefreshScores(); // Required in the case that the beatmap hasn't changed
});
[SetUpSteps]
public void SetupSteps()
{
// Ensure the leaderboard has finished async-loading drawables
AddUntilStep("wait for drawables", () => leaderboard.ChildrenOfType<LeaderboardScore>().Any());
// Ensure the leaderboard items have finished showing up
AddStep("finish transforms", () => leaderboard.FinishTransforms(true));
}
[Test]
public void TestDeleteViaRightClick()
{
ScoreInfo scoreBeingDeleted = null;
AddStep("open menu for top score", () =>
{
var leaderboardScore = leaderboard.ChildrenOfType<LeaderboardScore>().First();
scoreBeingDeleted = leaderboardScore.Score;
InputManager.MoveMouseTo(leaderboardScore);
InputManager.Click(MouseButton.Right);
});
// Ensure the context menu has finished showing
AddStep("finish transforms", () => contextMenuContainer.FinishTransforms(true));
AddStep("click delete option", () =>
{
InputManager.MoveMouseTo(contextMenuContainer.ChildrenOfType<DrawableOsuMenuItem>().First(i => i.Item.Text.Value.ToString().ToLowerInvariant() == "delete"));
InputManager.Click(MouseButton.Left);
});
// Ensure the dialog has finished showing
AddStep("finish transforms", () => dialogOverlay.FinishTransforms(true));
AddStep("click delete button", () =>
{
InputManager.MoveMouseTo(dialogOverlay.ChildrenOfType<DialogButton>().First());
InputManager.Click(MouseButton.Left);
});
AddUntilStep("wait for fetch", () => leaderboard.Scores != null);
AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineID != scoreBeingDeleted.OnlineID));
}
[Test]
public void TestDeleteViaDatabase()
{
AddStep("delete top score", () => scoreManager.Delete(importedScores[0]));
AddUntilStep("wait for fetch", () => leaderboard.Scores != null);
AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineID != importedScores[0].OnlineID));
}
}
}

View File

@ -2,7 +2,9 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Testing;
using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
@ -11,13 +13,21 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestSceneDialogOverlay : OsuTestScene
{
public TestSceneDialogOverlay()
private DialogOverlay overlay;
[SetUpSteps]
public void SetUpSteps()
{
DialogOverlay overlay;
AddStep("create dialog overlay", () => Child = overlay = new DialogOverlay());
}
Add(overlay = new DialogOverlay());
[Test]
public void TestBasic()
{
TestPopupDialog firstDialog = null;
TestPopupDialog secondDialog = null;
AddStep("dialog #1", () => overlay.Push(new TestPopupDialog
AddStep("dialog #1", () => overlay.Push(firstDialog = new TestPopupDialog
{
Icon = FontAwesome.Regular.TrashAlt,
HeaderText = @"Confirm deletion of",
@ -37,7 +47,9 @@ namespace osu.Game.Tests.Visual.UserInterface
},
}));
AddStep("dialog #2", () => overlay.Push(new TestPopupDialog
AddAssert("first dialog displayed", () => overlay.CurrentDialog == firstDialog);
AddStep("dialog #2", () => overlay.Push(secondDialog = new TestPopupDialog
{
Icon = FontAwesome.Solid.Cog,
HeaderText = @"What do you want to do with",
@ -70,6 +82,46 @@ namespace osu.Game.Tests.Visual.UserInterface
},
},
}));
AddAssert("second dialog displayed", () => overlay.CurrentDialog == secondDialog);
AddAssert("first dialog is not part of hierarchy", () => firstDialog.Parent == null);
}
[Test]
public void TestDismissBeforePush()
{
TestPopupDialog testDialog = null;
AddStep("dismissed dialog push", () =>
{
overlay.Push(testDialog = new TestPopupDialog
{
State = { Value = Visibility.Hidden }
});
});
AddAssert("no dialog pushed", () => overlay.CurrentDialog == null);
AddAssert("dialog is not part of hierarchy", () => testDialog.Parent == null);
}
[Test]
public void TestDismissBeforePushViaButtonPress()
{
TestPopupDialog testDialog = null;
AddStep("dismissed dialog push", () =>
{
overlay.Push(testDialog = new TestPopupDialog
{
Buttons = new PopupDialogButton[]
{
new PopupDialogOkButton { Text = @"OK" },
},
});
testDialog.PerformOkAction();
});
AddAssert("no dialog pushed", () => overlay.CurrentDialog == null);
AddAssert("dialog is not part of hierarchy", () => testDialog.Parent == null);
}
private class TestPopupDialog : PopupDialog

View File

@ -14,11 +14,6 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneFooterButtonMods : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(FooterButtonMods)
};
private readonly TestFooterButtonMods footerButtonMods;
public TestSceneFooterButtonMods()
@ -41,9 +36,9 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep(@"Add DoubleTime", () => changeMods(doubleTimeMod));
AddAssert(@"Check DoubleTime multiplier", () => assertModsMultiplier(doubleTimeMod));
var mutlipleIncrementMods = new Mod[] { new OsuModDoubleTime(), new OsuModHidden(), new OsuModHardRock() };
AddStep(@"Add multiple Mods", () => changeMods(mutlipleIncrementMods));
AddAssert(@"Check multiple mod multiplier", () => assertModsMultiplier(mutlipleIncrementMods));
var multipleIncrementMods = new Mod[] { new OsuModDoubleTime(), new OsuModHidden(), new OsuModHardRock() };
AddStep(@"Add multiple Mods", () => changeMods(multipleIncrementMods));
AddAssert(@"Check multiple mod multiplier", () => assertModsMultiplier(multipleIncrementMods));
}
[Test]
@ -78,10 +73,10 @@ namespace osu.Game.Tests.Visual.UserInterface
private bool assertModsMultiplier(IEnumerable<Mod> mods)
{
var multiplier = mods.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier);
var expectedValue = multiplier.Equals(1.0) ? string.Empty : $"{multiplier:N2}x";
double multiplier = mods.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier);
string expectedValue = multiplier.Equals(1.0) ? string.Empty : $"{multiplier:N2}x";
return expectedValue == footerButtonMods.MultiplierText.Text;
return expectedValue == footerButtonMods.MultiplierText.Current.Value;
}
private class TestFooterButtonMods : FooterButtonMods

View File

@ -0,0 +1,53 @@
// 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.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Overlays.Dashboard.Friends;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneFriendsOnlineStatusControl : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private FriendOnlineStreamControl control;
[SetUp]
public void SetUp() => Schedule(() => Child = control = new FriendOnlineStreamControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
[Test]
public void Populate()
{
AddStep("Populate", () => control.Populate(new List<APIUser>
{
new APIUser
{
IsOnline = true
},
new APIUser
{
IsOnline = false
},
new APIUser
{
IsOnline = false
}
}));
AddAssert("3 users", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.All)?.Count == 3);
AddAssert("1 online user", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.Online)?.Count == 1);
AddAssert("2 offline users", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.Offline)?.Count == 2);
}
}
}

View File

@ -1,11 +1,8 @@
// 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;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Menu;
@ -15,12 +12,6 @@ namespace osu.Game.Tests.Visual.UserInterface
{
protected override double TimePerAction => 100; // required for the early exit test, since hold-to-confirm delay is 200ms
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ExitConfirmOverlay),
typeof(HoldToConfirmContainer),
};
public TestSceneHoldToConfirmOverlay()
{
bool fired = false;

View File

@ -0,0 +1,131 @@
// 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;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osuTK.Graphics;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneLabelledColourPalette : OsuManualInputManagerTestScene
{
private LabelledColourPalette component;
[Test]
public void TestPalette([Values] bool hasDescription)
{
createColourPalette(hasDescription);
AddRepeatStep("add random colour", () => component.Colours.Add(randomColour()), 4);
AddStep("set custom prefix", () => component.ColourNamePrefix = "Combo");
AddRepeatStep("remove random colour", () =>
{
if (component.Colours.Count > 0)
component.Colours.RemoveAt(RNG.Next(component.Colours.Count));
}, 8);
}
[Test]
public void TestUserInteractions()
{
createColourPalette();
assertColourCount(4);
clickAddColour();
assertColourCount(5);
deleteFirstColour();
assertColourCount(4);
clickFirstColour();
AddAssert("colour picker spawned", () => this.ChildrenOfType<OsuColourPicker>().Any());
}
private void createColourPalette(bool hasDescription = false)
{
AddStep("create component", () =>
{
Child = new PopoverContainer
{
RelativeSizeAxes = Axes.Both,
Child = new OsuContextMenuContainer
{
RelativeSizeAxes = Axes.Both,
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 500,
AutoSizeAxes = Axes.Y,
Child = component = new LabelledColourPalette
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
ColourNamePrefix = "My colour #"
}
}
}
};
component.Label = "a sample component";
component.Description = hasDescription ? "this text describes the component" : string.Empty;
component.Colours.AddRange(new[]
{
Colour4.DarkRed,
Colour4.Aquamarine,
Colour4.Goldenrod,
Colour4.Gainsboro
});
});
}
private Colour4 randomColour() => new Color4(
RNG.NextSingle(),
RNG.NextSingle(),
RNG.NextSingle(),
1);
private void assertColourCount(int count) => AddAssert($"colour count is {count}", () => component.Colours.Count == count);
private void clickAddColour() => AddStep("click new colour button", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<ColourPalette.AddColourButton>().Single());
InputManager.Click(MouseButton.Left);
});
private void clickFirstColour() => AddStep("click first colour", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<ColourDisplay>().First());
InputManager.Click(MouseButton.Left);
});
private void deleteFirstColour()
{
AddStep("right-click first colour", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<ColourDisplay>().First());
InputManager.Click(MouseButton.Right);
});
AddUntilStep("wait for menu", () => this.ChildrenOfType<OsuContextMenu>().Any());
AddStep("click delete", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<DrawableOsuMenuItem>().Single());
InputManager.Click(MouseButton.Left);
});
}
}
}

View File

@ -1,12 +1,16 @@
// 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;
using NUnit.Framework;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterfaceV2;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
@ -21,12 +25,51 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestCase(true)]
public void TestNonPadded(bool hasDescription) => createPaddedComponent(hasDescription, false);
[Test]
public void TestFixedWidth()
{
const float label_width = 200;
AddStep("create components", () => Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
new NonPaddedLabelledDrawable
{
Label = "short",
FixedLabelWidth = label_width
},
new NonPaddedLabelledDrawable
{
Label = "very very very very very very very very very very very long",
FixedLabelWidth = label_width
},
new PaddedLabelledDrawable
{
Label = "short",
FixedLabelWidth = label_width
},
new PaddedLabelledDrawable
{
Label = "very very very very very very very very very very very long",
FixedLabelWidth = label_width
}
}
});
AddStep("unset label width", () => this.ChildrenOfType<LabelledDrawable<Drawable>>().ForEach(d => d.FixedLabelWidth = null));
AddStep("reset label width", () => this.ChildrenOfType<LabelledDrawable<Drawable>>().ForEach(d => d.FixedLabelWidth = label_width));
}
private void createPaddedComponent(bool hasDescription = false, bool padded = true)
{
LabelledDrawable<Drawable> component = null;
AddStep("create component", () =>
{
LabelledDrawable<Drawable> component;
Child = new Container
{
Anchor = Anchor.Centre,
@ -39,6 +82,8 @@ namespace osu.Game.Tests.Visual.UserInterface
component.Label = "a sample component";
component.Description = hasDescription ? "this text describes the component" : string.Empty;
});
AddAssert($"description {(hasDescription ? "visible" : "hidden")}", () => component.ChildrenOfType<TextFlowContainer>().ElementAt(1).IsPresent == hasDescription);
}
private class PaddedLabelledDrawable : LabelledDrawable<Drawable>

View File

@ -0,0 +1,34 @@
// 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 NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterfaceV2;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneLabelledDropdown : OsuTestScene
{
[Test]
public void TestLabelledDropdown()
=> AddStep(@"create dropdown", () => Child = new LabelledDropdown<string>
{
Label = @"Countdown speed",
Items = new[]
{
@"Half",
@"Normal",
@"Double"
},
Description = @"This is a description"
});
[Test]
public void TestLabelledEnumDropdown()
=> AddStep(@"create dropdown", () => Child = new LabelledEnumDropdown<BeatmapOnlineStatus>
{
Label = @"Beatmap status",
Description = @"This is a description"
});
}
}

View File

@ -0,0 +1,84 @@
// 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;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneLabelledSliderBar : OsuTestScene
{
[TestCase(false)]
[TestCase(true)]
public void TestSliderBar(bool hasDescription) => createSliderBar(hasDescription);
private void createSliderBar(bool hasDescription = false)
{
AddStep("create component", () =>
{
FillFlowContainer flow;
Child = flow = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 500,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new LabelledSliderBar<double>
{
Current = new BindableDouble(5)
{
MinValue = 0,
MaxValue = 10,
Precision = 1,
},
Label = "a sample component",
Description = hasDescription ? "this text describes the component" : string.Empty,
},
},
};
foreach (var colour in Enum.GetValues(typeof(OverlayColourScheme)).OfType<OverlayColourScheme>())
{
flow.Add(new OverlayColourContainer(colour)
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = new LabelledSliderBar<double>
{
Current = new BindableDouble(5)
{
MinValue = 0,
MaxValue = 10,
Precision = 1,
},
Label = "a sample component",
Description = hasDescription ? "this text describes the component" : string.Empty,
}
});
}
});
}
private class OverlayColourContainer : Container
{
[Cached]
private OverlayColourProvider colourProvider;
public OverlayColourContainer(OverlayColourScheme scheme)
{
colourProvider = new OverlayColourProvider(scheme);
}
}
}
}

View File

@ -1,8 +1,6 @@
// 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;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -12,12 +10,6 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneLabelledSwitchButton : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(LabelledSwitchButton),
typeof(SwitchButton)
};
[TestCase(false)]
[TestCase(true)]
public void TestSwitchButton(bool hasDescription) => createSwitchButton(hasDescription);

View File

@ -1,8 +1,6 @@
// 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;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@ -14,11 +12,6 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestSceneLabelledTextBox : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(LabelledTextBox),
};
[TestCase(false)]
[TestCase(true)]
public void TestTextBox(bool hasDescription) => createTextBox(hasDescription);

View File

@ -0,0 +1,98 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneLoadingLayer : OsuTestScene
{
private TestLoadingLayer overlay;
private Container content;
[SetUp]
public void SetUp() => Schedule(() =>
{
Children = new[]
{
content = new Container
{
Size = new Vector2(300),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
Colour = Color4.SlateGray,
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.9f),
Children = new Drawable[]
{
new OsuSpriteText { Text = "Sample content" },
new TriangleButton { Text = "can't puush me", Width = 200, },
new TriangleButton { Text = "puush me", Width = 200, Action = () => { } },
}
},
overlay = new TestLoadingLayer(true),
}
},
};
});
[Test]
public void TestShowHide()
{
AddAssert("not visible", () => !overlay.IsPresent);
AddStep("show", () => overlay.Show());
AddUntilStep("wait for content dim", () => overlay.BackgroundDimLayer.Alpha > 0);
AddStep("hide", () => overlay.Hide());
AddUntilStep("wait for content restore", () => Precision.AlmostEquals(overlay.BackgroundDimLayer.Alpha, 0));
}
[Test]
public void TestLargeArea()
{
AddStep("show", () =>
{
content.RelativeSizeAxes = Axes.Both;
content.Size = new Vector2(1);
overlay.Show();
});
AddStep("hide", () => overlay.Hide());
}
private class TestLoadingLayer : LoadingLayer
{
public new Box BackgroundDimLayer => base.BackgroundDimLayer;
public TestLoadingLayer(bool dimBackground = false, bool withBox = true)
: base(dimBackground, withBox)
{
}
}
}
}

View File

@ -8,12 +8,12 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneLoadingAnimation : OsuGridTestScene
public class TestSceneLoadingSpinner : OsuGridTestScene
{
public TestSceneLoadingAnimation()
public TestSceneLoadingSpinner()
: base(2, 2)
{
LoadingAnimation loading;
LoadingSpinner loading;
Cell(0).AddRange(new Drawable[]
{
@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both
},
loading = new LoadingAnimation()
loading = new LoadingSpinner()
});
loading.Show();
@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Colour = Color4.White,
RelativeSizeAxes = Axes.Both
},
loading = new LoadingAnimation()
loading = new LoadingSpinner(true)
});
loading.Show();
@ -46,14 +46,14 @@ namespace osu.Game.Tests.Visual.UserInterface
Colour = Color4.Gray,
RelativeSizeAxes = Axes.Both
},
loading = new LoadingAnimation()
loading = new LoadingSpinner()
});
loading.Show();
Cell(3).AddRange(new Drawable[]
{
loading = new LoadingAnimation()
loading = new LoadingSpinner()
});
Scheduler.AddDelayed(() => loading.ToggleVisibility(), 200, true);

View File

@ -0,0 +1,46 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneLogoAnimation : OsuTestScene
{
[BackgroundDependencyLoader]
private void load(LargeTextureStore textures)
{
LogoAnimation anim2;
Add(anim2 = new LogoAnimation
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Texture = textures.Get("Intro/Triangles/logo-highlight"),
Colour = Colour4.White,
});
LogoAnimation anim;
Add(anim = new LogoAnimation
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Texture = textures.Get("Intro/Triangles/logo-background"),
Colour = OsuColour.Gray(0.6f),
});
AddSliderStep("Progress", 0f, 1f, 0f, newValue =>
{
anim2.AnimationProgress = newValue;
anim.AnimationProgress = newValue;
});
}
}
}

View File

@ -2,17 +2,15 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Framework.Testing;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osuTK;
using osuTK.Graphics;
@ -20,17 +18,6 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneLogoTrackingContainer : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(PlayerLoader),
typeof(Player),
typeof(LogoTrackingContainer),
typeof(ButtonSystem),
typeof(ButtonSystemState),
typeof(Menu),
typeof(MainMenu)
};
private OsuLogo logo;
private TestLogoTrackingContainer trackingContainer;
private Container transferContainer;
@ -277,7 +264,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private void moveLogoFacade()
{
if (logoFacade?.Transforms.Count == 0 && transferContainer?.Transforms.Count == 0)
if (!logoFacade.Transforms.Any() && !transferContainer.Transforms.Any())
{
Random random = new Random();
trackingContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300);

View File

@ -0,0 +1,64 @@
// 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 osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneModButton : OsuTestScene
{
public TestSceneModButton()
{
Children = new Drawable[]
{
new ModButton(new MultiMod(new TestMod1(), new TestMod2(), new TestMod3(), new TestMod4()))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
}
};
}
private class TestMod1 : TestMod
{
public override string Name => "Test mod 1";
public override string Acronym => "M1";
}
private class TestMod2 : TestMod
{
public override string Name => "Test mod 2";
public override string Acronym => "M2";
public override IconUsage? Icon => FontAwesome.Solid.Exclamation;
}
private class TestMod3 : TestMod
{
public override string Name => "Test mod 3";
public override string Acronym => "M3";
public override IconUsage? Icon => FontAwesome.Solid.ArrowRight;
}
private class TestMod4 : TestMod
{
public override string Name => "Test mod 4";
public override string Acronym => "M4";
}
private abstract class TestMod : Mod, IApplicableMod
{
public override double ScoreMultiplier => 1.0;
public override string Description => "This is a test mod.";
}
}
}

View File

@ -0,0 +1,281 @@
// 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.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneModDifficultyAdjustSettings : OsuManualInputManagerTestScene
{
private OsuModDifficultyAdjust modDifficultyAdjust;
[SetUpSteps]
public void SetUpSteps()
{
AddStep("create control", () =>
{
modDifficultyAdjust = new OsuModDifficultyAdjust();
Child = new Container
{
Size = new Vector2(300),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
ChildrenEnumerable = modDifficultyAdjust.CreateSettingsControls(),
},
}
};
});
}
[Test]
public void TestFollowsBeatmapDefaultsVisually()
{
setBeatmapWithDifficultyParameters(5);
checkSliderAtValue("Circle Size", 5);
checkBindableAtValue("Circle Size", null);
setBeatmapWithDifficultyParameters(8);
checkSliderAtValue("Circle Size", 8);
checkBindableAtValue("Circle Size", null);
}
[Test]
public void TestOutOfRangeValueStillApplied()
{
AddStep("set override cs to 11", () => modDifficultyAdjust.CircleSize.Value = 11);
checkSliderAtValue("Circle Size", 11);
checkBindableAtValue("Circle Size", 11);
// this is a no-op, just showing that it won't reset the value during deserialisation.
setExtendedLimits(false);
checkSliderAtValue("Circle Size", 11);
checkBindableAtValue("Circle Size", 11);
// setting extended limits will reset the serialisation exception.
// this should be fine as the goal is to allow, at most, the value of extended limits.
setExtendedLimits(true);
checkSliderAtValue("Circle Size", 11);
checkBindableAtValue("Circle Size", 11);
}
[Test]
public void TestExtendedLimits()
{
setSliderValue("Circle Size", 99);
checkSliderAtValue("Circle Size", 10);
checkBindableAtValue("Circle Size", 10);
setExtendedLimits(true);
checkSliderAtValue("Circle Size", 10);
checkBindableAtValue("Circle Size", 10);
setSliderValue("Circle Size", 99);
checkSliderAtValue("Circle Size", 11);
checkBindableAtValue("Circle Size", 11);
setExtendedLimits(false);
checkSliderAtValue("Circle Size", 10);
checkBindableAtValue("Circle Size", 10);
}
[Test]
public void TestUserOverrideMaintainedOnBeatmapChange()
{
setSliderValue("Circle Size", 9);
setBeatmapWithDifficultyParameters(2);
checkSliderAtValue("Circle Size", 9);
checkBindableAtValue("Circle Size", 9);
}
[Test]
public void TestResetToDefault()
{
setBeatmapWithDifficultyParameters(2);
setSliderValue("Circle Size", 9);
checkSliderAtValue("Circle Size", 9);
checkBindableAtValue("Circle Size", 9);
resetToDefault("Circle Size");
checkSliderAtValue("Circle Size", 2);
checkBindableAtValue("Circle Size", null);
}
[Test]
public void TestUserOverrideMaintainedOnMatchingBeatmapValue()
{
setBeatmapWithDifficultyParameters(3);
checkSliderAtValue("Circle Size", 3);
checkBindableAtValue("Circle Size", null);
// need to initially change it away from the current beatmap value to trigger an override.
setSliderValue("Circle Size", 4);
setSliderValue("Circle Size", 3);
checkSliderAtValue("Circle Size", 3);
checkBindableAtValue("Circle Size", 3);
setBeatmapWithDifficultyParameters(4);
checkSliderAtValue("Circle Size", 3);
checkBindableAtValue("Circle Size", 3);
}
[Test]
public void TestResetToDefaults()
{
setBeatmapWithDifficultyParameters(5);
setSliderValue("Circle Size", 3);
setExtendedLimits(true);
checkSliderAtValue("Circle Size", 3);
checkBindableAtValue("Circle Size", 3);
AddStep("reset mod settings", () => modDifficultyAdjust.ResetSettingsToDefaults());
checkSliderAtValue("Circle Size", 5);
checkBindableAtValue("Circle Size", null);
}
[Test]
public void TestModSettingChangeTracker()
{
ModSettingChangeTracker tracker = null;
Queue<Mod> settingsChangedQueue = null;
setBeatmapWithDifficultyParameters(5);
AddStep("add mod settings change tracker", () =>
{
settingsChangedQueue = new Queue<Mod>();
tracker = new ModSettingChangeTracker(modDifficultyAdjust.Yield())
{
SettingChanged = settingsChangedQueue.Enqueue
};
});
AddAssert("no settings changed", () => settingsChangedQueue.Count == 0);
setSliderValue("Circle Size", 3);
settingsChangedFired();
setSliderValue("Circle Size", 5);
checkBindableAtValue("Circle Size", 5);
settingsChangedFired();
AddStep("reset mod settings", () => modDifficultyAdjust.CircleSize.SetDefault());
checkBindableAtValue("Circle Size", null);
settingsChangedFired();
setExtendedLimits(true);
settingsChangedFired();
AddStep("dispose tracker", () =>
{
tracker.Dispose();
tracker = null;
});
void settingsChangedFired()
{
AddAssert("setting changed event fired", () =>
{
settingsChangedQueue.Dequeue();
return settingsChangedQueue.Count == 0;
});
}
}
private void resetToDefault(string name)
{
AddStep($"Reset {name} to default", () =>
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
.Current.SetDefault());
}
private void setExtendedLimits(bool status) =>
AddStep($"Set extended limits {status}", () => modDifficultyAdjust.ExtendedLimits.Value = status);
private void setSliderValue(string name, float value)
{
AddStep($"Set {name} slider to {value}", () =>
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
.ChildrenOfType<SettingsSlider<float>>().First().Current.Value = value);
}
private void checkBindableAtValue(string name, float? expectedValue)
{
AddAssert($"Bindable {name} is {(expectedValue?.ToString() ?? "null")}", () =>
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
.Current.Value == expectedValue);
}
private void checkSliderAtValue(string name, float expectedValue)
{
AddAssert($"Slider {name} at {expectedValue}", () =>
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
.ChildrenOfType<SettingsSlider<float>>().First().Current.Value == expectedValue);
}
private void setBeatmapWithDifficultyParameters(float value)
{
AddStep($"set beatmap with all {value}", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = value,
CircleSize = value,
DrainRate = value,
ApproachRate = value,
}
}
}));
}
}
}

View File

@ -0,0 +1,38 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Play.HUD;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneModDisplay : OsuTestScene
{
[Test]
public void TestMode([Values] ExpansionMode mode)
{
AddStep("create mod display", () =>
{
Child = new ModDisplay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
ExpansionMode = mode,
Current =
{
Value = new Mod[]
{
new OsuModHardRock(),
new OsuModDoubleTime(),
new OsuModDifficultyAdjust(),
new OsuModEasy(),
}
}
};
});
}
}
}

View File

@ -0,0 +1,49 @@
// 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;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Play.HUD;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneModFlowDisplay : OsuTestScene
{
private ModFlowDisplay modFlow;
[SetUp]
public void SetUp() => Schedule(() =>
{
Child = modFlow = new ModFlowDisplay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.None,
Width = 200,
Current =
{
Value = new OsuRuleset().CreateAllMods().ToArray(),
}
};
});
[Test]
public void TestWrapping()
{
AddSliderStep("icon size", 0.1f, 2, 1, val =>
{
if (modFlow != null)
modFlow.IconScale = val;
});
AddSliderStep("flow width", 100, 500, 200, val =>
{
if (modFlow != null)
modFlow.Width = val;
});
}
}
}

View File

@ -0,0 +1,34 @@
// 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;
using NUnit.Framework;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneModIcon : OsuTestScene
{
[Test]
public void TestChangeModType()
{
ModIcon icon = null;
AddStep("create mod icon", () => Child = icon = new ModIcon(new OsuModDoubleTime()));
AddStep("change mod", () => icon.Mod = new OsuModEasy());
}
[Test]
public void TestInterfaceModType()
{
ModIcon icon = null;
var ruleset = new OsuRuleset();
AddStep("create mod icon", () => Child = icon = new ModIcon(ruleset.AllMods.First(m => m.Acronym == "DT")));
AddStep("change mod", () => icon.Mod = ruleset.AllMods.First(m => m.Acronym == "EZ"));
}
}
}

View File

@ -8,18 +8,17 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Mods.Sections;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play.HUD;
using osuTK;
using osuTK.Graphics;
@ -29,20 +28,6 @@ namespace osu.Game.Tests.Visual.UserInterface
[Description("mod select and icon display")]
public class TestSceneModSelectOverlay : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ModDisplay),
typeof(ModSection),
typeof(ModIcon),
typeof(ModButton),
typeof(ModButtonEmpty),
typeof(DifficultyReductionSection),
typeof(DifficultyIncreaseSection),
typeof(AutomationSection),
typeof(ConversionSection),
typeof(FunSection),
};
private RulesetStore rulesets;
private ModDisplay modDisplay;
private TestModSelectOverlay modSelect;
@ -56,24 +41,8 @@ namespace osu.Game.Tests.Visual.UserInterface
[SetUp]
public void SetUp() => Schedule(() =>
{
Children = new Drawable[]
{
modSelect = new TestModSelectOverlay
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
},
modDisplay = new ModDisplay
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Position = new Vector2(0, 25),
Current = { BindTarget = modSelect.SelectedMods }
}
};
SelectedMods.Value = Array.Empty<Mod>();
createDisplay(() => new TestModSelectOverlay());
});
[SetUpSteps]
@ -82,6 +51,82 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("show", () => modSelect.Show());
}
/// <summary>
/// Ensure that two mod overlays are not cross polluting via central settings instances.
/// </summary>
[Test]
public void TestSettingsNotCrossPolluting()
{
Bindable<IReadOnlyList<Mod>> selectedMods2 = null;
AddStep("select diff adjust", () => SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() });
AddStep("set setting", () => modSelect.ChildrenOfType<SettingsSlider<float>>().First().Current.Value = 8);
AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType<OsuModDifficultyAdjust>().Single().CircleSize.Value == 8);
AddStep("create second bindable", () => selectedMods2 = new Bindable<IReadOnlyList<Mod>>(new Mod[] { new OsuModDifficultyAdjust() }));
AddStep("create second overlay", () =>
{
Add(modSelect = new TestModSelectOverlay().With(d =>
{
d.Origin = Anchor.TopCentre;
d.Anchor = Anchor.TopCentre;
d.SelectedMods.BindTarget = selectedMods2;
}));
});
AddStep("show", () => modSelect.Show());
AddAssert("ensure first is unchanged", () => SelectedMods.Value.OfType<OsuModDifficultyAdjust>().Single().CircleSize.Value == 8);
AddAssert("ensure second is default", () => selectedMods2.Value.OfType<OsuModDifficultyAdjust>().Single().CircleSize.Value == null);
}
[Test]
public void TestSettingsResetOnDeselection()
{
var osuModDoubleTime = new OsuModDoubleTime { SpeedChange = { Value = 1.2 } };
changeRuleset(0);
AddStep("set dt mod with custom rate", () => { SelectedMods.Value = new[] { osuModDoubleTime }; });
AddAssert("selected mod matches", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.Value == 1.2);
AddStep("deselect", () => modSelect.DeselectAllButton.TriggerClick());
AddAssert("selected mods empty", () => SelectedMods.Value.Count == 0);
AddStep("reselect", () => modSelect.GetModButton(osuModDoubleTime).TriggerClick());
AddAssert("selected mod has default value", () => (SelectedMods.Value.Single() as OsuModDoubleTime)?.SpeedChange.IsDefault == true);
}
[Test]
public void TestAnimationFlushOnClose()
{
changeRuleset(0);
AddStep("Select all fun mods", () =>
{
modSelect.ModSectionsContainer
.Single(c => c.ModType == ModType.DifficultyIncrease)
.SelectAll();
});
AddUntilStep("many mods selected", () => modDisplay.Current.Value.Count >= 5);
AddStep("trigger deselect and close overlay", () =>
{
modSelect.ModSectionsContainer
.Single(c => c.ModType == ModType.DifficultyIncrease)
.DeselectAll();
modSelect.Hide();
});
AddAssert("all mods deselected", () => modDisplay.Current.Value.Count == 0);
}
[Test]
public void TestOsuMods()
{
@ -93,12 +138,9 @@ namespace osu.Game.Tests.Visual.UserInterface
var harderMods = osu.GetModsFor(ModType.DifficultyIncrease);
var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail);
var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden);
var doubleTimeMod = harderMods.OfType<MultiMod>().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime));
var spunOutMod = easierMods.FirstOrDefault(m => m is OsuModSpunOut);
var easy = easierMods.FirstOrDefault(m => m is OsuModEasy);
var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock);
@ -106,10 +148,6 @@ namespace osu.Game.Tests.Visual.UserInterface
testMultiMod(doubleTimeMod);
testIncompatibleMods(easy, hardRock);
testDeselectAll(easierMods.Where(m => !(m is MultiMod)));
testMultiplierTextColour(noFailMod, () => modSelect.LowMultiplierColour);
testMultiplierTextColour(hiddenMod, () => modSelect.HighMultiplierColour);
testUnimplementedMod(spunOutMod);
}
[Test]
@ -117,7 +155,11 @@ namespace osu.Game.Tests.Visual.UserInterface
{
changeRuleset(3);
testRankedText(new ManiaRuleset().GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom));
var mania = new ManiaRuleset();
testModsWithSameBaseType(
mania.CreateMod<ManiaModFadeIn>(),
mania.CreateMod<ManiaModHidden>());
}
[Test]
@ -131,7 +173,7 @@ namespace osu.Game.Tests.Visual.UserInterface
changeRuleset(0);
AddAssert("ensure mods still selected", () => modDisplay.Current.Value.Single(m => m is OsuModNoFail) != null);
AddAssert("ensure mods still selected", () => modDisplay.Current.Value.SingleOrDefault(m => m is OsuModNoFail) != null);
changeRuleset(3);
@ -142,6 +184,112 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0);
}
[Test]
public void TestExternallySetCustomizedMod()
{
changeRuleset(0);
AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } });
AddAssert("ensure button is selected and customized accordingly", () =>
{
var button = modSelect.GetModButton(SelectedMods.Value.Single());
return ((OsuModDoubleTime)button.SelectedMod).SpeedChange.Value == 1.01;
});
}
[Test]
public void TestSettingsAreRetainedOnReload()
{
changeRuleset(0);
AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } });
AddAssert("setting remains", () => (SelectedMods.Value.SingleOrDefault() as OsuModDoubleTime)?.SpeedChange.Value == 1.01);
AddStep("create overlay", () => createDisplay(() => new TestNonStackedModSelectOverlay()));
AddAssert("setting remains", () => (SelectedMods.Value.SingleOrDefault() as OsuModDoubleTime)?.SpeedChange.Value == 1.01);
}
[Test]
public void TestExternallySetModIsReplacedByOverlayInstance()
{
Mod external = new OsuModDoubleTime();
Mod overlayButtonMod = null;
changeRuleset(0);
AddStep("set mod externally", () => { SelectedMods.Value = new[] { external }; });
AddAssert("ensure button is selected", () =>
{
var button = modSelect.GetModButton(SelectedMods.Value.Single());
overlayButtonMod = button.SelectedMod;
return overlayButtonMod.GetType() == external.GetType();
});
// Right now, when an external change occurs, the ModSelectOverlay will replace the global instance with its own
AddAssert("mod instance doesn't match", () => external != overlayButtonMod);
AddAssert("one mod present in global selected", () => SelectedMods.Value.Count == 1);
AddAssert("globally selected matches button's mod instance", () => SelectedMods.Value.Contains(overlayButtonMod));
AddAssert("globally selected doesn't contain original external change", () => !SelectedMods.Value.Contains(external));
}
[Test]
public void TestNonStacked()
{
changeRuleset(0);
AddStep("create overlay", () => createDisplay(() => new TestNonStackedModSelectOverlay()));
AddStep("show", () => modSelect.Show());
AddAssert("ensure all buttons are spread out", () => modSelect.ChildrenOfType<ModButton>().All(m => m.Mods.Length <= 1));
}
[Test]
public void TestChangeIsValidChangesButtonVisibility()
{
changeRuleset(0);
AddAssert("double time visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModDoubleTime)));
AddStep("make double time invalid", () => modSelect.IsValidMod = m => !(m is OsuModDoubleTime));
AddUntilStep("double time not visible", () => modSelect.ChildrenOfType<ModButton>().All(b => !b.Mods.Any(m => m is OsuModDoubleTime)));
AddAssert("nightcore still visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModNightcore)));
AddStep("make double time valid again", () => modSelect.IsValidMod = m => true);
AddUntilStep("double time visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModDoubleTime)));
AddAssert("nightcore still visible", () => modSelect.ChildrenOfType<ModButton>().Any(b => b.Mods.Any(m => m is OsuModNightcore)));
}
[Test]
public void TestChangeIsValidPreservesSelection()
{
changeRuleset(0);
AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() });
AddAssert("DT + HD selected", () => modSelect.ChildrenOfType<ModButton>().Count(b => b.Selected) == 2);
AddStep("make NF invalid", () => modSelect.IsValidMod = m => !(m is ModNoFail));
AddAssert("DT + HD still selected", () => modSelect.ChildrenOfType<ModButton>().Count(b => b.Selected) == 2);
}
[Test]
public void TestUnimplementedModIsUnselectable()
{
var testRuleset = new TestUnimplementedModOsuRuleset();
changeTestRuleset(testRuleset.RulesetInfo);
var conversionMods = testRuleset.GetModsFor(ModType.Conversion);
var unimplementedMod = conversionMods.FirstOrDefault(m => m is TestUnimplementedMod);
testUnimplementedMod(unimplementedMod);
}
private void testSingleMod(Mod mod)
{
selectNext(mod);
@ -205,24 +353,16 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("check for no selection", () => !modSelect.SelectedMods.Value.Any());
}
private void testMultiplierTextColour(Mod mod, Func<Color4> getCorrectColour)
private void testModsWithSameBaseType(Mod modA, Mod modB)
{
checkLabelColor(() => Color4.White);
selectNext(mod);
AddWaitStep("wait for changing colour", 1);
checkLabelColor(getCorrectColour);
selectPrevious(mod);
AddWaitStep("wait for changing colour", 1);
checkLabelColor(() => Color4.White);
}
selectNext(modA);
checkSelected(modA);
selectNext(modB);
checkSelected(modB);
private void testRankedText(Mod mod)
{
AddUntilStep("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0);
selectNext(mod);
AddUntilStep("check for unranked", () => modSelect.UnrankedLabel.Alpha != 0);
selectPrevious(mod);
AddUntilStep("check for ranked", () => modSelect.UnrankedLabel.Alpha == 0);
// Backwards
selectPrevious(modA);
checkSelected(modA);
}
private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1));
@ -234,13 +374,19 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert($"check {mod.Name} is selected", () =>
{
var button = modSelect.GetModButton(mod);
return modSelect.SelectedMods.Value.Single(m => m.Name == mod.Name) != null && button.SelectedMod.GetType() == mod.GetType() && button.Selected;
return modSelect.SelectedMods.Value.SingleOrDefault(m => m.Name == mod.Name) != null && button.SelectedMod.GetType() == mod.GetType() && button.Selected;
});
}
private void changeRuleset(int? id)
private void changeRuleset(int? onlineId)
{
AddStep($"change ruleset to {(id?.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == id); });
AddStep($"change ruleset to {(onlineId?.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.OnlineID == onlineId); });
waitForLoad();
}
private void changeTestRuleset(RulesetInfo rulesetInfo)
{
AddStep($"change ruleset to {rulesetInfo.Name}", () => { Ruleset.Value = rulesetInfo; });
waitForLoad();
}
@ -256,26 +402,69 @@ namespace osu.Game.Tests.Visual.UserInterface
});
}
private void checkLabelColor(Func<Color4> getColour) => AddAssert("check label has expected colour", () => modSelect.MultiplierLabel.Colour.AverageColour == getColour());
private void createDisplay(Func<TestModSelectOverlay> createOverlayFunc)
{
Children = new Drawable[]
{
modSelect = createOverlayFunc().With(d =>
{
d.Origin = Anchor.BottomCentre;
d.Anchor = Anchor.BottomCentre;
d.SelectedMods.BindTarget = SelectedMods;
}),
modDisplay = new ModDisplay
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Position = new Vector2(-5, 25),
Current = { BindTarget = modSelect.SelectedMods }
}
};
}
private class TestModSelectOverlay : ModSelectOverlay
private class TestModSelectOverlay : UserModSelectOverlay
{
public new Bindable<IReadOnlyList<Mod>> SelectedMods => base.SelectedMods;
public bool AllLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded);
public new FillFlowContainer<ModSection> ModSectionsContainer =>
base.ModSectionsContainer;
public ModButton GetModButton(Mod mod)
{
var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type);
return section.ButtonsContainer.OfType<ModButton>().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType()));
}
public new OsuSpriteText MultiplierLabel => base.MultiplierLabel;
public new OsuSpriteText UnrankedLabel => base.UnrankedLabel;
public new TriangleButton DeselectAllButton => base.DeselectAllButton;
public new Color4 LowMultiplierColour => base.LowMultiplierColour;
public new Color4 HighMultiplierColour => base.HighMultiplierColour;
}
private class TestNonStackedModSelectOverlay : TestModSelectOverlay
{
protected override bool Stacked => false;
}
private class TestUnimplementedMod : Mod
{
public override string Name => "Unimplemented mod";
public override string Acronym => "UM";
public override string Description => "A mod that is not implemented.";
public override double ScoreMultiplier => 1;
public override ModType Type => ModType.Conversion;
}
private class TestUnimplementedModOsuRuleset : OsuRuleset
{
public override IEnumerable<Mod> GetModsFor(ModType type)
{
if (type == ModType.Conversion) return base.GetModsFor(type).Concat(new[] { new TestUnimplementedMod() });
return base.GetModsFor(type);
}
}
}
}

View File

@ -8,6 +8,8 @@ using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
@ -15,29 +17,40 @@ using osu.Game.Overlays.Mods;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.UI;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneModSettings : OsuTestScene
public class TestSceneModSettings : OsuManualInputManagerTestScene
{
private TestModSelectOverlay modSelect;
private readonly Mod testCustomisableMod = new TestModCustomisable1();
private readonly Mod testCustomisableAutoOpenMod = new TestModCustomisable2();
[SetUp]
public void SetUp() => Schedule(() =>
{
SelectedMods.Value = Array.Empty<Mod>();
Ruleset.Value = CreateTestRulesetInfo();
});
[Test]
public void TestButtonShowsOnCustomisableMod()
{
createModSelect();
openModSelect();
AddStep("open", () => modSelect.Show());
AddAssert("button disabled", () => !modSelect.CustomiseButton.Enabled.Value);
AddUntilStep("wait for button load", () => modSelect.ButtonsLoaded);
AddStep("select mod", () => modSelect.SelectMod(testCustomisableMod));
AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value);
AddStep("open Customisation", () => modSelect.CustomiseButton.Click());
AddStep("open Customisation", () => modSelect.CustomiseButton.TriggerClick());
AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableMod));
AddAssert("controls hidden", () => modSelect.ModSettingsContainer.Alpha == 0);
AddAssert("controls hidden", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden);
}
[Test]
@ -49,71 +62,140 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("mods still active", () => SelectedMods.Value.Count == 1);
AddStep("open", () => modSelect.Show());
openModSelect();
AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value);
}
[Test]
public void TestCustomisationMenuVisibility()
{
createModSelect();
openModSelect();
AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden);
AddStep("select mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod));
AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.State.Value == Visibility.Visible);
AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableAutoOpenMod));
AddAssert("Customisation closed", () => modSelect.ModSettingsContainer.State.Value == Visibility.Hidden);
}
[Test]
public void TestModSettingsUnboundWhenCopied()
{
OsuModDoubleTime original = null;
OsuModDoubleTime copy = null;
AddStep("create mods", () =>
{
original = new OsuModDoubleTime();
copy = (OsuModDoubleTime)original.DeepClone();
});
AddStep("change property", () => original.SpeedChange.Value = 2);
AddAssert("original has new value", () => Precision.AlmostEquals(2.0, original.SpeedChange.Value));
AddAssert("copy has original value", () => Precision.AlmostEquals(1.5, copy.SpeedChange.Value));
}
[Test]
public void TestMultiModSettingsUnboundWhenCopied()
{
MultiMod original = null;
MultiMod copy = null;
AddStep("create mods", () =>
{
original = new MultiMod(new OsuModDoubleTime());
copy = (MultiMod)original.DeepClone();
});
AddStep("change property", () => ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value = 2);
AddAssert("original has new value", () => Precision.AlmostEquals(2.0, ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value));
AddAssert("copy has original value", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)copy.Mods[0]).SpeedChange.Value));
}
[Test]
public void TestCustomisationMenuNoClickthrough()
{
createModSelect();
openModSelect();
AddStep("change mod settings menu width to full screen", () => modSelect.SetModSettingsWidth(1.0f));
AddStep("select cm2", () => modSelect.SelectMod(testCustomisableAutoOpenMod));
AddAssert("Customisation opened", () => modSelect.ModSettingsContainer.State.Value == Visibility.Visible);
AddStep("hover over mod behind settings menu", () => InputManager.MoveMouseTo(modSelect.GetModButton(testCustomisableMod)));
AddAssert("Mod is not considered hovered over", () => !modSelect.GetModButton(testCustomisableMod).IsHovered);
AddStep("left click mod", () => InputManager.Click(MouseButton.Left));
AddAssert("only cm2 is active", () => SelectedMods.Value.Count == 1);
AddStep("right click mod", () => InputManager.Click(MouseButton.Right));
AddAssert("only cm2 is active", () => SelectedMods.Value.Count == 1);
}
private void createModSelect()
{
AddStep("create mod select", () =>
{
Ruleset.Value = new TestRulesetInfo();
Child = modSelect = new TestModSelectOverlay
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
SelectedMods = { BindTarget = SelectedMods }
};
});
}
private class TestModSelectOverlay : ModSelectOverlay
private void openModSelect()
{
public new Container ModSettingsContainer => base.ModSettingsContainer;
AddStep("open", () => modSelect.Show());
AddUntilStep("wait for ready", () => modSelect.State.Value == Visibility.Visible && modSelect.ButtonsLoaded);
}
private class TestModSelectOverlay : UserModSelectOverlay
{
public new VisibilityContainer ModSettingsContainer => base.ModSettingsContainer;
public new TriangleButton CustomiseButton => base.CustomiseButton;
public bool ButtonsLoaded => ModSectionsContainer.Children.All(c => c.ModIconsLoaded);
public ModButton GetModButton(Mod mod)
{
return ModSectionsContainer.ChildrenOfType<ModButton>().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType()));
}
public void SelectMod(Mod mod) =>
ModSectionsContainer.Children.Single(s => s.ModType == mod.Type)
.ButtonsContainer.OfType<ModButton>().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())).SelectNext(1);
GetModButton(mod).SelectNext(1);
public void SetModSettingsWidth(float newWidth) =>
ModSettingsContainer.Parent.Width = newWidth;
}
public class TestRulesetInfo : RulesetInfo
public static RulesetInfo CreateTestRulesetInfo() => new TestCustomisableModRuleset().RulesetInfo;
public class TestCustomisableModRuleset : Ruleset
{
public override Ruleset CreateInstance() => new TestCustomisableModRuleset();
public TestRulesetInfo()
public override IEnumerable<Mod> GetModsFor(ModType type)
{
Available = true;
}
public class TestCustomisableModRuleset : Ruleset
{
public override IEnumerable<Mod> GetModsFor(ModType type)
if (type == ModType.Conversion)
{
if (type == ModType.Conversion)
return new Mod[]
{
return new Mod[]
{
new TestModCustomisable1(),
new TestModCustomisable2()
};
}
return Array.Empty<Mod>();
new TestModCustomisable1(),
new TestModCustomisable2()
};
}
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new NotImplementedException();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException();
public override string Description { get; } = "test";
public override string ShortName { get; } = "tst";
return Array.Empty<Mod>();
}
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new NotImplementedException();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException();
public override string Description { get; } = "test";
public override string ShortName { get; } = "tst";
}
private class TestModCustomisable1 : TestModCustomisable
@ -128,12 +210,16 @@ namespace osu.Game.Tests.Visual.UserInterface
public override string Name => "Customisable Mod 2";
public override string Acronym => "CM2";
public override bool RequiresConfiguration => true;
}
private abstract class TestModCustomisable : Mod, IApplicableMod
{
public override double ScoreMultiplier => 1.0;
public override string Description => "This is a customisable test mod.";
public override ModType Type => ModType.Conversion;
[SettingSource("Sample float", "Change something for a mod")]

View File

@ -1,14 +1,13 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
@ -18,16 +17,6 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestSceneNotificationOverlay : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(NotificationSection),
typeof(SimpleNotification),
typeof(ProgressNotification),
typeof(ProgressCompletionNotification),
typeof(IHasCompletionTarget),
typeof(Notification)
};
private NotificationOverlay notificationOverlay;
private readonly List<ProgressNotification> progressingNotifications = new List<ProgressNotification>();
@ -52,6 +41,44 @@ namespace osu.Game.Tests.Visual.UserInterface
notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; };
});
[Test]
public void TestCompleteProgress()
{
ProgressNotification notification = null;
AddStep("add progress notification", () =>
{
notification = new ProgressNotification
{
Text = @"Uploading to BSS...",
CompletionText = "Uploaded to BSS!",
};
notificationOverlay.Post(notification);
progressingNotifications.Add(notification);
});
AddUntilStep("wait completion", () => notification.State == ProgressNotificationState.Completed);
}
[Test]
public void TestCancelProgress()
{
ProgressNotification notification = null;
AddStep("add progress notification", () =>
{
notification = new ProgressNotification
{
Text = @"Uploading to BSS...",
CompletionText = "Uploaded to BSS!",
};
notificationOverlay.Post(notification);
progressingNotifications.Add(notification);
});
AddWaitStep("wait 3", 3);
AddStep("cancel notification", () => notification.State = ProgressNotificationState.Cancelled);
}
[Test]
public void TestBasicFlow()
{
@ -116,6 +143,15 @@ namespace osu.Game.Tests.Visual.UserInterface
checkDisplayedCount(3);
}
[Test]
public void TestError()
{
setState(Visibility.Visible);
AddStep(@"error #1", sendErrorNotification);
AddAssert("Is visible", () => notificationOverlay.State.Value == Visibility.Visible);
checkDisplayedCount(1);
}
[Test]
public void TestSpam()
{
@ -140,7 +176,7 @@ namespace osu.Game.Tests.Visual.UserInterface
foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active))
{
if (n.Progress < 1)
n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle();
n.Progress += (float)(Time.Elapsed / 2000);
else
n.State = ProgressNotificationState.Completed;
}
@ -190,7 +226,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private void sendBarrage()
{
switch (RNG.Next(0, 4))
switch (RNG.Next(0, 5))
{
case 0:
sendHelloNotification();
@ -207,6 +243,10 @@ namespace osu.Game.Tests.Visual.UserInterface
case 3:
sendDownloadProgress();
break;
case 4:
sendErrorNotification();
break;
}
}
@ -225,6 +265,11 @@ namespace osu.Game.Tests.Visual.UserInterface
notificationOverlay.Post(new BackgroundNotification { Text = @"Welcome to osu!. Enjoy your stay!" });
}
private void sendErrorNotification()
{
notificationOverlay.Post(new SimpleErrorNotification { Text = @"Rut roh!. Something went wrong!" });
}
private void sendManyNotifications()
{
for (int i = 0; i < 10; i++)

View File

@ -4,8 +4,8 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Overlays;
using osu.Game.Rulesets.Osu;
namespace osu.Game.Tests.Visual.UserInterface
{
@ -15,22 +15,29 @@ namespace osu.Game.Tests.Visual.UserInterface
[Cached]
private MusicController musicController = new MusicController();
public TestSceneNowPlayingOverlay()
{
Clock = new FramedClock();
private NowPlayingOverlay nowPlayingOverlay;
var np = new NowPlayingOverlay
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
nowPlayingOverlay = new NowPlayingOverlay
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre
};
Add(musicController);
Add(np);
Add(nowPlayingOverlay);
}
AddStep(@"show", () => np.Show());
[Test]
public void TestShowHideDisable()
{
AddStep(@"show", () => nowPlayingOverlay.Show());
AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state);
AddStep(@"show", () => np.Hide());
AddStep(@"hide", () => nowPlayingOverlay.Hide());
}
}
}

View File

@ -1,55 +0,0 @@
// 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;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneNumberBox : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuNumberBox),
};
private OsuNumberBox numberBox;
[BackgroundDependencyLoader]
private void load()
{
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding { Horizontal = 250 },
Child = numberBox = new OsuNumberBox
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
PlaceholderText = "Insert numbers here"
}
};
clearInput();
AddStep("enter numbers", () => numberBox.Text = "987654321");
expectedValue("987654321");
clearInput();
AddStep("enter text + single number", () => numberBox.Text = "1 hello 2 world 3");
expectedValue("123");
clearInput();
}
private void clearInput() => AddStep("clear input", () => numberBox.Text = null);
private void expectedValue(string value) => AddAssert("expect number", () => numberBox.Text == value);
}
}

View File

@ -29,10 +29,10 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("Display toast with lengthy text", () => osd.Display(new LengthyToast()));
AddAssert("Toast width is greater than 240", () => osd.Child.Width > 240);
AddRepeatStep("Change toggle (no bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingNoKeybind), 2);
AddRepeatStep("Change toggle (with bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingWithKeybind), 2);
AddRepeatStep("Change enum (no bind)", () => config.IncrementEnumSetting(TestConfigSetting.EnumSettingNoKeybind), 3);
AddRepeatStep("Change enum (with bind)", () => config.IncrementEnumSetting(TestConfigSetting.EnumSettingWithKeybind), 3);
AddRepeatStep("Change toggle (no bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingNoKeyBind), 2);
AddRepeatStep("Change toggle (with bind)", () => config.ToggleSetting(TestConfigSetting.ToggleSettingWithKeyBind), 2);
AddRepeatStep("Change enum (no bind)", () => config.IncrementEnumSetting(TestConfigSetting.EnumSettingNoKeyBind), 3);
AddRepeatStep("Change enum (with bind)", () => config.IncrementEnumSetting(TestConfigSetting.EnumSettingWithKeyBind), 3);
}
private class TestConfigManager : ConfigManager<TestConfigSetting>
@ -44,30 +44,30 @@ namespace osu.Game.Tests.Visual.UserInterface
protected override void InitialiseDefaults()
{
Set(TestConfigSetting.ToggleSettingNoKeybind, false);
Set(TestConfigSetting.EnumSettingNoKeybind, EnumSetting.Setting1);
Set(TestConfigSetting.ToggleSettingWithKeybind, false);
Set(TestConfigSetting.EnumSettingWithKeybind, EnumSetting.Setting1);
SetDefault(TestConfigSetting.ToggleSettingNoKeyBind, false);
SetDefault(TestConfigSetting.EnumSettingNoKeyBind, EnumSetting.Setting1);
SetDefault(TestConfigSetting.ToggleSettingWithKeyBind, false);
SetDefault(TestConfigSetting.EnumSettingWithKeyBind, EnumSetting.Setting1);
base.InitialiseDefaults();
}
public void ToggleSetting(TestConfigSetting setting) => Set(setting, !Get<bool>(setting));
public void ToggleSetting(TestConfigSetting setting) => SetValue(setting, !Get<bool>(setting));
public void IncrementEnumSetting(TestConfigSetting setting)
{
var nextValue = Get<EnumSetting>(setting) + 1;
if (nextValue > EnumSetting.Setting4)
nextValue = EnumSetting.Setting1;
Set(setting, nextValue);
SetValue(setting, nextValue);
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
{
new TrackedSetting<bool>(TestConfigSetting.ToggleSettingNoKeybind, b => new SettingDescription(b, "toggle setting with no keybind", b ? "enabled" : "disabled")),
new TrackedSetting<EnumSetting>(TestConfigSetting.EnumSettingNoKeybind, v => new SettingDescription(v, "enum setting with no keybind", v.ToString())),
new TrackedSetting<bool>(TestConfigSetting.ToggleSettingWithKeybind, b => new SettingDescription(b, "toggle setting with keybind", b ? "enabled" : "disabled", "fake keybind")),
new TrackedSetting<EnumSetting>(TestConfigSetting.EnumSettingWithKeybind, v => new SettingDescription(v, "enum setting with keybind", v.ToString(), "fake keybind")),
new TrackedSetting<bool>(TestConfigSetting.ToggleSettingNoKeyBind, b => new SettingDescription(b, "toggle setting with no keybind", b ? "enabled" : "disabled")),
new TrackedSetting<EnumSetting>(TestConfigSetting.EnumSettingNoKeyBind, v => new SettingDescription(v, "enum setting with no keybind", v.ToString())),
new TrackedSetting<bool>(TestConfigSetting.ToggleSettingWithKeyBind, b => new SettingDescription(b, "toggle setting with keybind", b ? "enabled" : "disabled", "fake keybind")),
new TrackedSetting<EnumSetting>(TestConfigSetting.EnumSettingWithKeyBind, v => new SettingDescription(v, "enum setting with keybind", v.ToString(), "fake keybind")),
};
protected override void PerformLoad()
@ -79,10 +79,10 @@ namespace osu.Game.Tests.Visual.UserInterface
private enum TestConfigSetting
{
ToggleSettingNoKeybind,
EnumSettingNoKeybind,
ToggleSettingWithKeybind,
EnumSettingWithKeybind
ToggleSettingNoKeyBind,
EnumSettingNoKeyBind,
ToggleSettingWithKeyBind,
EnumSettingWithKeyBind
}
private enum EnumSetting

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
@ -9,47 +10,96 @@ using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOsuAnimatedButton : OsuGridTestScene
public class TestSceneOsuAnimatedButton : OsuTestScene
{
public TestSceneOsuAnimatedButton()
: base(3, 2)
[Test]
public void TestRelativeSized()
{
Cell(0).Add(new BaseContainer("relative sized")
AddStep("add button", () => Child = new BaseContainer("relative sized")
{
RelativeSizeAxes = Axes.Both,
Action = () => { }
});
}
Cell(1).Add(new BaseContainer("auto sized")
[Test]
public void TestAutoSized()
{
AddStep("add button", () => Child = new BaseContainer("auto sized")
{
AutoSizeAxes = Axes.Both
AutoSizeAxes = Axes.Both,
Action = () => { }
});
}
Cell(2).Add(new BaseContainer("relative Y auto X")
[Test]
public void TestRelativeYAutoX()
{
AddStep("add button", () => Child = new BaseContainer("relative Y auto X")
{
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X
AutoSizeAxes = Axes.X,
Action = () => { }
});
}
Cell(3).Add(new BaseContainer("relative X auto Y")
[Test]
public void TestRelativeXAutoY()
{
AddStep("add button", () => Child = new BaseContainer("relative X auto Y")
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
AutoSizeAxes = Axes.Y,
Action = () => { }
});
}
Cell(4).Add(new BaseContainer("fixed")
[Test]
public void TestFixed1()
{
AddStep("add button", () => Child = new BaseContainer("fixed")
{
Size = new Vector2(100),
Action = () => { }
});
}
Cell(5).Add(new BaseContainer("fixed")
[Test]
public void TestFixed2()
{
AddStep("add button", () => Child = new BaseContainer("fixed")
{
Size = new Vector2(100, 50),
Action = () => { }
});
}
[Test]
public void TestToggleEnabled()
{
BaseContainer button = null;
AddStep("add button", () => Child = button = new BaseContainer("fixed")
{
Size = new Vector2(200),
});
AddToggleStep("toggle enabled", toggle =>
{
for (int i = 0; i < 6; i++)
((BaseContainer)Cell(i).Child).Action = toggle ? () => { } : (Action)null;
button.Action = toggle ? () => { } : (Action)null;
});
}
[Test]
public void TestInitiallyDisabled()
{
AddStep("add disabled button", () =>
{
Child = new BaseContainer("disabled")
{
Size = new Vector2(100)
};
});
}

View File

@ -0,0 +1,46 @@
// 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;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOsuButton : OsuTestScene
{
[Test]
public void TestToggleEnabled()
{
OsuButton button = null;
AddStep("add button", () => Child = button = new OsuButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200),
Text = "Button"
});
AddToggleStep("toggle enabled", toggle =>
{
for (int i = 0; i < 6; i++)
button.Action = toggle ? () => { } : (Action)null;
});
}
[Test]
public void TestInitiallyDisabled()
{
AddStep("add button", () => Child = new OsuButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(200),
Text = "Button"
});
}
}
}

View File

@ -0,0 +1,20 @@
// 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 osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOsuDropdown : ThemeComparisonTestScene
{
protected override Drawable CreateContent() =>
new OsuEnumDropdown<BeatmapOnlineStatus>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 150
};
}
}

View File

@ -0,0 +1,77 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOsuFont : OsuTestScene
{
private OsuSpriteText spriteText;
private readonly BindableBool useAlternates = new BindableBool();
private readonly Bindable<FontWeight> weight = new Bindable<FontWeight>(FontWeight.Regular);
[BackgroundDependencyLoader]
private void load()
{
Child = spriteText = new OsuSpriteText
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.X,
AllowMultiline = true,
};
}
protected override void LoadComplete()
{
base.LoadComplete();
useAlternates.BindValueChanged(_ => updateFont());
weight.BindValueChanged(_ => updateFont(), true);
}
private void updateFont()
{
FontUsage usage = useAlternates.Value ? OsuFont.TorusAlternate : OsuFont.Torus;
spriteText.Font = usage.With(size: 40, weight: weight.Value);
}
[Test]
public void TestTorusAlternates()
{
AddStep("set all ASCII letters", () => spriteText.Text = @"ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz");
AddStep("set all alternates", () => spriteText.Text = @"A Á Ă Â Ä À Ā Ą Å Ã
Æ B D Ð Ď Đ E É Ě Ê
Ë Ė È Ē Ę F G Ğ Ģ Ġ
H I Í Î Ï İ Ì Ī Į K
Ķ O Œ P Þ Q R Ŕ Ř Ŗ
T Ŧ Ť Ţ Ț V W Ẃ Ŵ Ẅ
Ẁ X Y Ý Ŷ Ÿ Ỳ a á ă
â ä à ā ą å ã æ b d
ď đ e é ě ê ë ė è ē
ę f g ğ ģ ġ k ķ m n
ń ň ņ ŋ ñ o œ p þ q
t ŧ ť ţ ț u ú û ü ù
ű ū ų ů w ẃ ŵ ẅ ẁ x
y ý ŷ ÿ ỳ");
AddToggleStep("toggle alternates", alternates => useAlternates.Value = alternates);
addSetWeightStep(FontWeight.Light);
addSetWeightStep(FontWeight.Regular);
addSetWeightStep(FontWeight.SemiBold);
addSetWeightStep(FontWeight.Bold);
void addSetWeightStep(FontWeight newWeight) => AddStep($"set weight {newWeight}", () => weight.Value = newWeight);
}
}
}

View File

@ -12,7 +12,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneOsuHoverContainer : ManualInputManagerTestScene
public class TestSceneOsuHoverContainer : OsuManualInputManagerTestScene
{
private OsuHoverTestContainer hoverContainer;
private Box colourContainer;

View File

@ -1,6 +1,7 @@
// 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.Diagnostics;
using System.Reflection;
using NUnit.Framework;
using osu.Framework.Extensions.IEnumerableExtensions;
@ -9,6 +10,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osuTK;
@ -45,7 +47,12 @@ namespace osu.Game.Tests.Visual.UserInterface
});
foreach (var p in typeof(OsuIcon).GetProperties(BindingFlags.Public | BindingFlags.Static))
flow.Add(new Icon($"{nameof(OsuIcon)}.{p.Name}", (IconUsage)p.GetValue(null)));
{
object propValue = p.GetValue(null);
Debug.Assert(propValue != null);
flow.Add(new Icon($"{nameof(OsuIcon)}.{p.Name}", (IconUsage)propValue));
}
AddStep("toggle shadows", () => flow.Children.ForEach(i => i.SpriteIcon.Shadow = !i.SpriteIcon.Shadow));
AddStep("change icons", () => flow.Children.ForEach(i => i.SpriteIcon.Icon = new IconUsage((char)(i.SpriteIcon.Icon.Icon + 1))));
@ -53,7 +60,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private class Icon : Container, IHasTooltip
{
public string TooltipText { get; }
public LocalisableString TooltipText { get; }
public SpriteIcon SpriteIcon { get; }

View File

@ -0,0 +1,25 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Screens.Menu;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOsuLogo : OsuTestScene
{
[Test]
public void TestBasic()
{
AddStep("Add logo", () =>
{
Child = new OsuLogo
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
});
}
}
}

View File

@ -0,0 +1,258 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers.Markdown;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOsuMarkdownContainer : OsuTestScene
{
private OsuMarkdownContainer markdownContainer;
[Cached]
private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Orange);
[SetUp]
public void Setup() => Schedule(() =>
{
Children = new Drawable[]
{
new Box
{
Colour = overlayColour.Background5,
RelativeSizeAxes = Axes.Both,
},
new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(20),
Child = markdownContainer = new OsuMarkdownContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
}
}
};
});
[Test]
public void TestEmphases()
{
AddStep("Emphases", () =>
{
markdownContainer.Text = @"_italic with underscore_
*italic with asterisk*
__bold with underscore__
**bold with asterisk**
*__italic with asterisk, bold with underscore__*
_**italic with underscore, bold with asterisk**_";
});
}
[Test]
public void TestHeading()
{
AddStep("Add Heading", () =>
{
markdownContainer.Text = @"# Header 1
## Header 2
### Header 3
#### Header 4
##### Header 5";
});
}
[Test]
public void TestLink()
{
AddStep("Add Link", () =>
{
markdownContainer.Text = "[Welcome to osu!](https://osu.ppy.sh)";
});
}
[Test]
public void TestLinkWithInlineText()
{
AddStep("Add Link with inline text", () =>
{
markdownContainer.Text = "Hey, [welcome to osu!](https://osu.ppy.sh) Please enjoy the game.";
});
}
[Test]
public void TestLinkWithTitle()
{
AddStep("Add Link with title", () =>
{
markdownContainer.Text = "[wikipedia](https://www.wikipedia.org \"The Free Encyclopedia\")";
});
}
[Test]
public void TestAutoLink()
{
AddStep("Add autolink", () =>
{
markdownContainer.Text = "<https://discord.gg/ppy>";
});
}
[Test]
public void TestInlineCode()
{
AddStep("Add inline code", () =>
{
markdownContainer.Text = "This is `inline code` text";
});
}
[Test]
public void TestParagraph()
{
AddStep("Add paragraph", () =>
{
markdownContainer.Text = @"first paragraph
second paragraph
third paragraph";
});
}
[Test]
public void TestFencedCodeBlock()
{
AddStep("Add Code Block", () =>
{
markdownContainer.Text = @"```markdown
# Markdown code block
This is markdown code block.
```";
});
}
[Test]
public void TestSeparator()
{
AddStep("Add Seperator", () =>
{
markdownContainer.Text = @"Line above
---
Line below";
});
}
[Test]
public void TestQuote()
{
AddStep("Add quote", () =>
{
markdownContainer.Text =
@"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.";
});
}
[Test]
public void TestTable()
{
AddStep("Add Table", () =>
{
markdownContainer.Text =
@"| Left Aligned | Center Aligned | Right Aligned |
| :------------------- | :--------------------: | ---------------------:|
| Long Align Left Text | Long Align Center Text | Long Align Right Text |
| Align Left | Align Center | Align Right |
| Left | Center | Right |";
});
}
[Test]
public void TestUnorderedList()
{
AddStep("Add Unordered List", () =>
{
markdownContainer.Text = @"- First item level 1
- Second item level 1
- First item level 2
- First item level 3
- Second item level 3
- Third item level 3
- First item level 4
- Second item level 4
- Third item level 4
- Second item level 2
- Third item level 2
- Third item level 1";
});
}
[Test]
public void TestOrderedList()
{
AddStep("Add Ordered List", () =>
{
markdownContainer.Text = @"1. First item level 1
2. Second item level 1
1. First item level 2
1. First item level 3
2. Second item level 3
3. Third item level 3
1. First item level 4
2. Second item level 4
3. Third item level 4
2. Second item level 2
3. Third item level 2
3. Third item level 1";
});
}
[Test]
public void TestLongMixedList()
{
AddStep("Add long mixed list", () =>
{
markdownContainer.Text = @"1. The osu! World Cup is a country-based team tournament played on the osu! game mode.
- While this competition is planned as a 4 versus 4 setup, this may change depending on the number of incoming registrations.
2. Beatmap scoring is based on Score V2.
3. The beatmaps for each round will be announced by the map selectors in advance on the Sunday before the actual matches take place. Only these beatmaps will be used during the respective matches.
- One beatmap will be a tiebreaker beatmap. This beatmap will only be played in case of a tie. **The only exception to this is the Qualifiers pool.**
4. The match schedule will be settled by the Tournament Management (see the [scheduling instructions](#scheduling-instructions)).
5. If no staff or referee is available, the match will be postponed.
6. Use of the Visual Settings to alter background dim or disable beatmap elements like storyboards and skins are allowed.
7. If the beatmap ends in a draw, the map will be nullified and replayed.
8. If a player disconnects, their scores will not be counted towards their team's total.
- Disconnects within 30 seconds or 25% of the beatmap length (whichever happens first) after beatmap begin can be aborted and/or rematched. This is up to the referee's discretion.
9. Beatmaps cannot be reused in the same match unless the map was nullified.
10. If less than the minimum required players attend, the maximum time the match can be postponed is 10 minutes.
11. Exchanging players during a match is allowed without limitations.
- **If a map rematch is required, exchanging players is not allowed. With the referee's discretion, an exception can be made if the previous roster is unavailable to play.**
12. Lag is not a valid reason to nullify a beatmap.
13. All players are supposed to keep the match running fluently and without delays. Penalties can be issued to the players if they cause excessive match delays.
14. If a player disconnects between maps and the team cannot provide a replacement, the match can be delayed 10 minutes at maximum.
15. All players and referees must be treated with respect. Instructions of the referees and tournament Management are to be followed. Decisions labeled as final are not to be objected.
16. Disrupting the match by foul play, insulting and provoking other players or referees, delaying the match or other deliberate inappropriate misbehavior is strictly prohibited.
17. The multiplayer chatrooms are subject to the [osu! community rules](/wiki/Rules).
- Breaking the chat rules will result in a silence. Silenced players can not participate in multiplayer matches and must be exchanged for the time being.
18. **The seeding method will be revealed after all the teams have played their Qualifier rounds.**
19. Unexpected incidents are handled by the tournament management. Referees may allow higher tolerance depending on the circumstances. This is up to their discretion.
20. Penalties for violating the tournament rules may include:
- Exclusion of specific players for one beatmap
- Exclusion of specific players for an entire match
- Declaring the match as Lost by Default
- Disqualification from the entire tournament
- Disqualification from the current and future official tournaments until appealed
- Any modification of these rules will be announced.";
});
}
}
}

View File

@ -0,0 +1,83 @@
// 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;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOsuMenu : OsuManualInputManagerTestScene
{
private OsuMenu menu;
private bool actionPerformed;
[SetUp]
public void Setup() => Schedule(() =>
{
actionPerformed = false;
Child = menu = new OsuMenu(Direction.Vertical, true)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Items = new[]
{
new OsuMenuItem("standard", MenuItemType.Standard, performAction),
new OsuMenuItem("highlighted", MenuItemType.Highlighted, performAction),
new OsuMenuItem("destructive", MenuItemType.Destructive, performAction),
}
};
});
[Test]
public void TestClickEnabledMenuItem()
{
AddStep("move to first menu item", () => InputManager.MoveMouseTo(menu.ChildrenOfType<DrawableOsuMenuItem>().First()));
AddStep("click", () => InputManager.Click(MouseButton.Left));
AddAssert("action performed", () => actionPerformed);
}
[Test]
public void TestDisableMenuItemsAndClick()
{
AddStep("disable menu items", () =>
{
foreach (var item in menu.Items)
((OsuMenuItem)item).Action.Disabled = true;
});
AddStep("move to first menu item", () => InputManager.MoveMouseTo(menu.ChildrenOfType<DrawableOsuMenuItem>().First()));
AddStep("click", () => InputManager.Click(MouseButton.Left));
AddAssert("action not performed", () => !actionPerformed);
}
[Test]
public void TestEnableMenuItemsAndClick()
{
AddStep("disable menu items", () =>
{
foreach (var item in menu.Items)
((OsuMenuItem)item).Action.Disabled = true;
});
AddStep("enable menu items", () =>
{
foreach (var item in menu.Items)
((OsuMenuItem)item).Action.Disabled = false;
});
AddStep("move to first menu item", () => InputManager.MoveMouseTo(menu.ChildrenOfType<DrawableOsuMenuItem>().First()));
AddStep("click", () => InputManager.Click(MouseButton.Left));
AddAssert("action performed", () => actionPerformed);
}
private void performAction() => actionPerformed = true;
}
}

View File

@ -0,0 +1,103 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOsuPopover : OsuGridTestScene
{
public TestSceneOsuPopover()
: base(1, 2)
{
Cell(0, 0).Child = new PopoverContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = @"No OverlayColourProvider",
Font = OsuFont.Default.With(size: 40)
},
new TriangleButtonWithPopover()
}
};
Cell(0, 1).Child = new ColourProvidingContainer(OverlayColourScheme.Orange)
{
RelativeSizeAxes = Axes.Both,
Child = new PopoverContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = @"With OverlayColourProvider (orange)",
Font = OsuFont.Default.With(size: 40)
},
new TriangleButtonWithPopover()
}
}
};
}
private class TriangleButtonWithPopover : TriangleButton, IHasPopover
{
public TriangleButtonWithPopover()
{
Width = 100;
Height = 30;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Text = @"open";
Action = this.ShowPopover;
}
public Popover GetPopover() => new OsuPopover
{
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
Children = new Drawable[]
{
new OsuSpriteText
{
Text = @"sample text"
},
new OsuTextBox
{
Width = 150,
Height = 30
}
}
}
};
}
private class ColourProvidingContainer : Container
{
[Cached]
private OverlayColourProvider provider { get; }
public ColourProvidingContainer(OverlayColourScheme colourScheme)
{
provider = new OverlayColourProvider(colourScheme);
}
}
}
}

View File

@ -0,0 +1,67 @@
// 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.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOsuTextBox : ThemeComparisonTestScene
{
private IEnumerable<OsuNumberBox> numberBoxes => this.ChildrenOfType<OsuNumberBox>();
protected override Drawable CreateContent() => new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding(50f),
Spacing = new Vector2(0f, 50f),
Children = new[]
{
new OsuTextBox
{
RelativeSizeAxes = Axes.X,
PlaceholderText = "Normal textbox",
},
new OsuPasswordTextBox
{
RelativeSizeAxes = Axes.X,
PlaceholderText = "Password textbox",
},
new OsuNumberBox
{
RelativeSizeAxes = Axes.X,
PlaceholderText = "Number textbox"
}
}
};
[Test]
public void TestNumberBox()
{
AddStep("create themed content", () => CreateThemedContent(OverlayColourScheme.Red));
clearTextboxes(numberBoxes);
AddStep("enter numbers", () => numberBoxes.ForEach(numberBox => numberBox.Text = "987654321"));
expectedValue(numberBoxes, "987654321");
clearTextboxes(numberBoxes);
AddStep("enter text + single number", () => numberBoxes.ForEach(numberBox => numberBox.Text = "1 hello 2 world 3"));
expectedValue(numberBoxes, "123");
clearTextboxes(numberBoxes);
}
private void clearTextboxes(IEnumerable<OsuTextBox> textBoxes) => AddStep("clear textbox", () => textBoxes.ForEach(textBox => textBox.Text = null));
private void expectedValue(IEnumerable<OsuTextBox> textBoxes, string value) => AddAssert("expected textbox value", () => textBoxes.All(textBox => textBox.Text == value));
}
}

View File

@ -0,0 +1,160 @@
// 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 osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Shapes;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOverlayHeader : OsuTestScene
{
private readonly FillFlowContainer flow;
public TestSceneOverlayHeader()
{
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = flow = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical
}
}
});
addHeader("Orange OverlayHeader (no background, 100 padding)", new TestNoBackgroundHeader(), OverlayColourScheme.Orange);
addHeader("Blue OverlayHeader (default 50 padding)", new TestNoControlHeader(), OverlayColourScheme.Blue);
addHeader("Green TabControlOverlayHeader (string) with ruleset selector", new TestStringTabControlHeader(), OverlayColourScheme.Green);
addHeader("Pink TabControlOverlayHeader (enum, 30 padding)", new TestEnumTabControlHeader(), OverlayColourScheme.Pink);
addHeader("Red BreadcrumbControlOverlayHeader (no background, 10 padding)", new TestBreadcrumbControlHeader(), OverlayColourScheme.Red);
}
private void addHeader(string name, OverlayHeader header, OverlayColourScheme colourScheme)
{
flow.Add(new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding(20),
Text = name,
},
new ColourProvidedContainer(colourScheme, header)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
}
}
});
}
private class ColourProvidedContainer : Container
{
[Cached]
private readonly OverlayColourProvider colourProvider;
public ColourProvidedContainer(OverlayColourScheme colourScheme, OverlayHeader header)
{
colourProvider = new OverlayColourProvider(colourScheme);
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
Add(header);
}
}
private class TestNoBackgroundHeader : OverlayHeader
{
protected override OverlayTitle CreateTitle() => new TestTitle();
public TestNoBackgroundHeader()
{
ContentSidePadding = 100;
}
}
private class TestNoControlHeader : OverlayHeader
{
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/changelog");
protected override OverlayTitle CreateTitle() => new TestTitle();
}
private class TestStringTabControlHeader : TabControlOverlayHeader<string>
{
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/news");
protected override OverlayTitle CreateTitle() => new TestTitle();
protected override Drawable CreateTitleContent() => new OverlayRulesetSelector();
public TestStringTabControlHeader()
{
TabControl.AddItem("tab1");
TabControl.AddItem("tab2");
}
}
private class TestEnumTabControlHeader : TabControlOverlayHeader<TestEnum>
{
public TestEnumTabControlHeader()
{
ContentSidePadding = 30;
}
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/rankings");
protected override OverlayTitle CreateTitle() => new TestTitle();
}
private enum TestEnum
{
Some,
Cool,
Tabs
}
private class TestBreadcrumbControlHeader : BreadcrumbControlOverlayHeader
{
protected override OverlayTitle CreateTitle() => new TestTitle();
public TestBreadcrumbControlHeader()
{
ContentSidePadding = 10;
TabControl.AddItem("tab1");
TabControl.AddItem("tab2");
TabControl.Current.Value = "tab2";
}
}
private class TestTitle : OverlayTitle
{
public TestTitle()
{
Title = "title";
IconTexture = "Icons/changelog";
}
}
}
}

View File

@ -0,0 +1,35 @@
// 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 osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Framework.Graphics;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOverlayHeaderBackground : OsuTestScene
{
public TestSceneOverlayHeaderBackground()
{
Add(new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 20),
Children = new[]
{
new OverlayHeaderBackground(@"Headers/changelog"),
new OverlayHeaderBackground(@"Headers/news"),
new OverlayHeaderBackground(@"Headers/rankings"),
new OverlayHeaderBackground(@"Headers/search"),
}
}
});
}
}
}

View File

@ -0,0 +1,74 @@
// 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 osu.Framework.Graphics;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Framework.Bindables;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using NUnit.Framework;
using osu.Framework.Graphics.Containers;
using osuTK;
using osu.Framework.Allocation;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOverlayRulesetSelector : OsuTestScene
{
private readonly OverlayRulesetSelector selector;
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
public TestSceneOverlayRulesetSelector()
{
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new[]
{
new ColourProvidedContainer(OverlayColourScheme.Green, selector = new OverlayRulesetSelector { Current = ruleset }),
new ColourProvidedContainer(OverlayColourScheme.Blue, new OverlayRulesetSelector { Current = ruleset }),
new ColourProvidedContainer(OverlayColourScheme.Orange, new OverlayRulesetSelector { Current = ruleset }),
new ColourProvidedContainer(OverlayColourScheme.Pink, new OverlayRulesetSelector { Current = ruleset }),
new ColourProvidedContainer(OverlayColourScheme.Purple, new OverlayRulesetSelector { Current = ruleset }),
new ColourProvidedContainer(OverlayColourScheme.Red, new OverlayRulesetSelector { Current = ruleset }),
}
});
}
private class ColourProvidedContainer : Container
{
[Cached]
private readonly OverlayColourProvider colourProvider;
public ColourProvidedContainer(OverlayColourScheme colourScheme, OverlayRulesetSelector rulesetSelector)
{
colourProvider = new OverlayColourProvider(colourScheme);
AutoSizeAxes = Axes.Both;
Add(rulesetSelector);
}
}
[Test]
public void TestSelection()
{
AddStep("Select osu!", () => ruleset.Value = new OsuRuleset().RulesetInfo);
AddAssert("Check osu! selected", () => selector.Current.Value.Equals(new OsuRuleset().RulesetInfo));
AddStep("Select mania", () => ruleset.Value = new ManiaRuleset().RulesetInfo);
AddAssert("Check mania selected", () => selector.Current.Value.Equals(new ManiaRuleset().RulesetInfo));
AddStep("Select taiko", () => ruleset.Value = new TaikoRuleset().RulesetInfo);
AddAssert("Check taiko selected", () => selector.Current.Value.Equals(new TaikoRuleset().RulesetInfo));
AddStep("Select catch", () => ruleset.Value = new CatchRuleset().RulesetInfo);
AddAssert("Check catch selected", () => selector.Current.Value.Equals(new CatchRuleset().RulesetInfo));
}
}
}

View File

@ -0,0 +1,106 @@
// 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 osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Framework.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Shapes;
using osuTK.Graphics;
using NUnit.Framework;
using osu.Framework.Utils;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneOverlayScrollContainer : OsuManualInputManagerTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private TestScrollContainer scroll;
private int invocationCount;
[SetUp]
public void SetUp() => Schedule(() =>
{
Child = scroll = new TestScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new Container
{
Height = 3000,
RelativeSizeAxes = Axes.X,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Gray
}
}
};
invocationCount = 0;
scroll.Button.Action += () => invocationCount++;
});
[Test]
public void TestButtonVisibility()
{
AddAssert("button is hidden", () => scroll.Button.State == Visibility.Hidden);
AddStep("scroll to end", () => scroll.ScrollToEnd(false));
AddAssert("button is visible", () => scroll.Button.State == Visibility.Visible);
AddStep("scroll to start", () => scroll.ScrollToStart(false));
AddAssert("button is hidden", () => scroll.Button.State == Visibility.Hidden);
AddStep("scroll to 500", () => scroll.ScrollTo(500));
AddUntilStep("scrolled to 500", () => Precision.AlmostEquals(scroll.Current, 500, 0.1f));
AddAssert("button is visible", () => scroll.Button.State == Visibility.Visible);
}
[Test]
public void TestButtonAction()
{
AddStep("scroll to end", () => scroll.ScrollToEnd(false));
AddStep("invoke action", () => scroll.Button.Action.Invoke());
AddUntilStep("scrolled back to start", () => Precision.AlmostEquals(scroll.Current, 0, 0.1f));
}
[Test]
public void TestClick()
{
AddStep("scroll to end", () => scroll.ScrollToEnd(false));
AddStep("click button", () =>
{
InputManager.MoveMouseTo(scroll.Button);
InputManager.Click(MouseButton.Left);
});
AddUntilStep("scrolled back to start", () => Precision.AlmostEquals(scroll.Current, 0, 0.1f));
}
[Test]
public void TestMultipleClicks()
{
AddStep("scroll to end", () => scroll.ScrollToEnd(false));
AddAssert("invocation count is 0", () => invocationCount == 0);
AddStep("hover button", () => InputManager.MoveMouseTo(scroll.Button));
AddRepeatStep("click button", () => InputManager.Click(MouseButton.Left), 3);
AddAssert("invocation count is 1", () => invocationCount == 1);
}
private class TestScrollContainer : OverlayScrollContainer
{
public new ScrollToTopButton Button => base.Button;
}
}
}

View File

@ -0,0 +1,94 @@
// 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;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Music;
using osu.Game.Tests.Resources;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestScenePlaylistOverlay : OsuManualInputManagerTestScene
{
private readonly BindableList<BeatmapSetInfo> beatmapSets = new BindableList<BeatmapSetInfo>();
private PlaylistOverlay playlistOverlay;
private BeatmapSetInfo first;
[SetUp]
public void Setup() => Schedule(() =>
{
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(300, 500),
Child = playlistOverlay = new PlaylistOverlay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
State = { Value = Visibility.Visible }
}
};
beatmapSets.Clear();
for (int i = 0; i < 100; i++)
{
beatmapSets.Add(TestResources.CreateTestBeatmapSetInfo());
}
first = beatmapSets.First();
playlistOverlay.BeatmapSets.BindTo(beatmapSets);
});
[Test]
public void TestRearrangeItems()
{
AddUntilStep("wait for animations to complete", () => !playlistOverlay.Transforms.Any());
AddStep("hold 1st item handle", () =>
{
var handle = this.ChildrenOfType<OsuRearrangeableListItem<BeatmapSetInfo>.PlaylistItemHandle>().First();
InputManager.MoveMouseTo(handle.ScreenSpaceDrawQuad.Centre);
InputManager.PressButton(MouseButton.Left);
});
AddStep("drag to 5th", () =>
{
var item = this.ChildrenOfType<PlaylistItem>().ElementAt(4);
InputManager.MoveMouseTo(item.ScreenSpaceDrawQuad.Centre);
});
AddAssert("song 1 is 5th", () => beatmapSets[4] == first);
AddStep("release handle", () => InputManager.ReleaseButton(MouseButton.Left));
}
[Test]
public void TestFiltering()
{
AddStep("set filter to \"10\"", () =>
{
var filterControl = playlistOverlay.ChildrenOfType<FilterControl>().Single();
filterControl.Search.Current.Value = "10";
});
AddAssert("results filtered correctly",
() => playlistOverlay.ChildrenOfType<PlaylistItem>()
.Where(item => item.MatchingFilter)
.All(item => item.FilterTerms.Any(term => term.Contains("10"))));
}
}
}

View File

@ -1,12 +1,9 @@
// 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;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Dialog;
namespace osu.Game.Tests.Visual.UserInterface
@ -14,14 +11,6 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestScenePopupDialog : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(PopupDialogOkButton),
typeof(PopupDialogCancelButton),
typeof(PopupDialogButton),
typeof(DialogButton),
};
public TestScenePopupDialog()
{
AddStep("new popup", () =>

View File

@ -0,0 +1,80 @@
// 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 NUnit.Framework;
using osu.Game.Overlays.Profile.Sections;
using osu.Framework.Testing;
using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Overlays;
using osu.Framework.Allocation;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneProfileSubsectionHeader : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
private ProfileSubsectionHeader header;
[Test]
public void TestHiddenCounter()
{
AddStep("Create header", () => createHeader("Header with hidden counter", CounterVisibilityState.AlwaysHidden));
AddAssert("Value is 0", () => header.Current.Value == 0);
AddAssert("Counter is hidden", () => header.ChildrenOfType<CounterPill>().First().Alpha == 0);
AddStep("Set count 10", () => header.Current.Value = 10);
AddAssert("Value is 10", () => header.Current.Value == 10);
AddAssert("Counter is hidden", () => header.ChildrenOfType<CounterPill>().First().Alpha == 0);
}
[Test]
public void TestVisibleCounter()
{
AddStep("Create header", () => createHeader("Header with visible counter", CounterVisibilityState.AlwaysVisible));
AddAssert("Value is 0", () => header.Current.Value == 0);
AddAssert("Counter is visible", () => header.ChildrenOfType<CounterPill>().First().Alpha == 1);
AddStep("Set count 10", () => header.Current.Value = 10);
AddAssert("Value is 10", () => header.Current.Value == 10);
AddAssert("Counter is visible", () => header.ChildrenOfType<CounterPill>().First().Alpha == 1);
}
[Test]
public void TestVisibleWhenZeroCounter()
{
AddStep("Create header", () => createHeader("Header with visible when zero counter", CounterVisibilityState.VisibleWhenZero));
AddAssert("Value is 0", () => header.Current.Value == 0);
AddAssert("Counter is visible", () => header.ChildrenOfType<CounterPill>().First().Alpha == 1);
AddStep("Set count 10", () => header.Current.Value = 10);
AddAssert("Value is 10", () => header.Current.Value == 10);
AddAssert("Counter is hidden", () => header.ChildrenOfType<CounterPill>().First().Alpha == 0);
AddStep("Set count 0", () => header.Current.Value = 0);
AddAssert("Value is 0", () => header.Current.Value == 0);
AddAssert("Counter is visible", () => header.ChildrenOfType<CounterPill>().First().Alpha == 1);
}
[Test]
public void TestInitialVisibility()
{
AddStep("Create header with 0 value", () => createHeader("Header with visible when zero counter", CounterVisibilityState.VisibleWhenZero));
AddAssert("Value is 0", () => header.Current.Value == 0);
AddAssert("Counter is visible", () => header.ChildrenOfType<CounterPill>().First().Alpha == 1);
AddStep("Create header with 1 value", () => createHeader("Header with visible when zero counter", CounterVisibilityState.VisibleWhenZero, 1));
AddAssert("Value is 1", () => header.Current.Value == 1);
AddAssert("Counter is hidden", () => header.ChildrenOfType<CounterPill>().First().Alpha == 0);
}
private void createHeader(string text, CounterVisibilityState state, int initialValue = 0)
{
Clear();
Add(header = new ProfileSubsectionHeader(text, state)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Current = { Value = initialValue }
});
}
}
}

View File

@ -0,0 +1,25 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Rankings;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneRankingsSortTabControl : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
public TestSceneRankingsSortTabControl()
{
Child = new RankingsSortTabControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
};
}
}
}

View File

@ -0,0 +1,44 @@
// 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;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.UserInterfaceV2;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneRoundedButton : OsuTestScene
{
[Test]
public void TestBasic()
{
RoundedButton button = null;
AddStep("create button", () => Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Colour4.DarkGray
},
button = new RoundedButton
{
Width = 400,
Text = "Test button",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = () => { }
}
}
});
AddToggleStep("toggle disabled", disabled => button.Action = disabled ? (Action)null : () => { });
}
}
}

View File

@ -1,7 +1,6 @@
// 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;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@ -26,7 +25,9 @@ namespace osu.Game.Tests.Visual.UserInterface
OsuSpriteText titleText;
IScreen startScreen = new TestScreenOne();
screenStack = new OsuScreenStack(startScreen) { RelativeSizeAxes = Axes.Both };
screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
screenStack.Push(startScreen);
Children = new Drawable[]
{
@ -62,7 +63,7 @@ namespace osu.Game.Tests.Visual.UserInterface
waitForCurrent();
pushNext();
waitForCurrent();
AddAssert(@"only 2 items", () => breadcrumbs.Items.Count() == 2);
AddAssert(@"only 2 items", () => breadcrumbs.Items.Count == 2);
AddStep(@"exit current", () => screenStack.CurrentScreen.Exit());
AddAssert(@"current screen is first", () => startScreen == screenStack.CurrentScreen);
}

View File

@ -0,0 +1,131 @@
// 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;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing;
using osu.Game.Graphics.Containers;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneSectionsContainer : OsuManualInputManagerTestScene
{
private readonly SectionsContainer<TestSection> container;
private float custom;
private const float header_height = 100;
public TestSceneSectionsContainer()
{
container = new SectionsContainer<TestSection>
{
RelativeSizeAxes = Axes.Y,
Width = 300,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
FixedHeader = new Box
{
Alpha = 0.5f,
Width = 300,
Height = header_height,
Colour = Color4.Red
}
};
container.SelectedSection.ValueChanged += section =>
{
if (section.OldValue != null)
section.OldValue.Selected = false;
if (section.NewValue != null)
section.NewValue.Selected = true;
};
Add(container);
}
[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));
AddStep("scroll up a bit", () => triggerUserScroll(0.1f));
AddStep("scroll down a bit", () => triggerUserScroll(-0.1f));
}
[Test]
public void TestCorrectSelectionAndVisibleTop()
{
const int sections_count = 11;
float[] alternating = { 0.07f, 0.33f, 0.16f, 0.33f };
AddStep("clear", () => container.Clear());
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]);
AddUntilStep("section top is visible", () =>
{
float scrollPosition = container.ChildrenOfType<UserTrackingScrollContainer>().First().Current;
float sectionTop = container.Children[scrollIndex].BoundingBox.Top;
return scrollPosition < sectionTop;
});
}
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]);
}
private static readonly ColourInfo selected_colour = ColourInfo.GradientVertical(Color4.Yellow, Color4.Gold);
private static readonly ColourInfo default_colour = ColourInfo.GradientVertical(Color4.White, Color4.DarkGray);
private void append(float multiplier)
{
container.Add(new TestSection
{
Width = 300,
Height = (container.ChildSize.Y - header_height) * multiplier,
Colour = default_colour
});
}
private void triggerUserScroll(float direction)
{
InputManager.MoveMouseTo(container);
InputManager.ScrollVerticalBy(direction);
}
private class TestSection : Box
{
public bool Selected
{
set => Colour = value ? selected_colour : default_colour;
}
}
}
}

View File

@ -0,0 +1,68 @@
// 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;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Overlays.Settings;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneSettingsCheckbox : OsuTestScene
{
[TestCase]
public void TestCheckbox()
{
AddStep("create component", () =>
{
FillFlowContainer flow;
Child = flow = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 500,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(5),
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new SettingsCheckbox
{
LabelText = "a sample component",
},
},
};
foreach (var colour1 in Enum.GetValues(typeof(OverlayColourScheme)).OfType<OverlayColourScheme>())
{
flow.Add(new OverlayColourContainer(colour1)
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = new SettingsCheckbox
{
LabelText = "a sample component",
}
});
}
});
}
private class OverlayColourContainer : Container
{
[Cached]
private OverlayColourProvider colourProvider;
public OverlayColourContainer(OverlayColourScheme scheme)
{
colourProvider = new OverlayColourProvider(scheme);
}
}
}
}

View File

@ -0,0 +1,71 @@
// 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;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneStarRatingDisplay : OsuTestScene
{
[TestCase(StarRatingDisplaySize.Regular)]
[TestCase(StarRatingDisplaySize.Small)]
public void TestDisplay(StarRatingDisplaySize size)
{
AddStep("load displays", () =>
{
Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(2f),
Direction = FillDirection.Horizontal,
ChildrenEnumerable = Enumerable.Range(0, 15).Select(i => new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(2f),
Direction = FillDirection.Vertical,
ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i * (i >= 11 ? 25f : 1f) + j * 0.1f, 0), size)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}),
})
};
});
}
[Test]
public void TestSpectrum()
{
StarRatingDisplay starRating = null;
AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1), animated: true)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(3f),
});
AddRepeatStep("set random value", () =>
{
starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1);
}, 10);
AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d =>
{
if (starRating != null)
starRating.Current.Value = new StarDifficulty(d, 1);
});
}
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -12,18 +11,10 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneStatefulMenuItem : ManualInputManagerTestScene
public class TestSceneStatefulMenuItem : OsuManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuMenu),
typeof(StatefulMenuItem),
typeof(TernaryStateMenuItem),
typeof(DrawableStatefulMenuItem),
};
[Test]
public void TestTernaryMenuItem()
public void TestTernaryRadioMenuItem()
{
OsuMenu menu = null;
@ -39,9 +30,57 @@ namespace osu.Game.Tests.Visual.UserInterface
Origin = Anchor.Centre,
Items = new[]
{
new TernaryStateMenuItem("First"),
new TernaryStateMenuItem("Second") { State = { BindTarget = state } },
new TernaryStateMenuItem("Third") { State = { Value = TernaryState.True } },
new TernaryStateRadioMenuItem("First"),
new TernaryStateRadioMenuItem("Second") { State = { BindTarget = state } },
new TernaryStateRadioMenuItem("Third") { State = { Value = TernaryState.True } },
}
};
});
checkState(TernaryState.Indeterminate);
click();
checkState(TernaryState.True);
click();
checkState(TernaryState.True);
click();
checkState(TernaryState.True);
AddStep("change state via bindable", () => state.Value = TernaryState.True);
void click() =>
AddStep("click", () =>
{
InputManager.MoveMouseTo(menu.ScreenSpaceDrawQuad.Centre);
InputManager.Click(MouseButton.Left);
});
void checkState(TernaryState expected)
=> AddAssert($"state is {expected}", () => state.Value == expected);
}
[Test]
public void TestTernaryToggleMenuItem()
{
OsuMenu menu = null;
Bindable<TernaryState> state = new Bindable<TernaryState>(TernaryState.Indeterminate);
AddStep("create menu", () =>
{
state.Value = TernaryState.Indeterminate;
Child = menu = new OsuMenu(Direction.Vertical, true)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Items = new[]
{
new TernaryStateToggleMenuItem("First"),
new TernaryStateToggleMenuItem("Second") { State = { BindTarget = state } },
new TernaryStateToggleMenuItem("Third") { State = { Value = TernaryState.True } },
}
};
});

View File

@ -9,7 +9,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneSwitchButton : ManualInputManagerTestScene
public class TestSceneSwitchButton : OsuManualInputManagerTestScene
{
private SwitchButton switchButton;

View File

@ -1,8 +1,6 @@
// 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;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
@ -10,13 +8,6 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneToggleMenuItem : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuMenu),
typeof(ToggleMenuItem),
typeof(DrawableStatefulMenuItem)
};
public TestSceneToggleMenuItem()
{
Add(new OsuMenu(Direction.Vertical, true)

View File

@ -3,25 +3,17 @@
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Toolbar;
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneToolbarRulesetSelector : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ToolbarRulesetSelector),
typeof(ToolbarRulesetTabButton),
};
[Resolved]
private RulesetStore rulesets { get; set; }
@ -44,7 +36,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("Select random", () =>
{
selector.Current.Value = selector.Items.ElementAt(RNG.Next(selector.Items.Count()));
selector.Current.Value = selector.Items.ElementAt(RNG.Next(selector.Items.Count));
});
AddStep("Toggle disabled state", () => selector.Current.Disabled = !selector.Current.Disabled);
}

View File

@ -12,6 +12,7 @@ using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
@ -24,16 +25,11 @@ namespace osu.Game.Tests.Visual.UserInterface
private BeatmapSetInfo testBeatmap;
private IAPIProvider api;
private RulesetStore rulesets;
[Resolved]
private BeatmapManager beatmaps { get; set; }
[BackgroundDependencyLoader]
private void load(OsuGameBase osu, IAPIProvider api, RulesetStore rulesets)
{
this.api = api;
this.rulesets = rulesets;
testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).Result;
}
@ -72,7 +68,7 @@ namespace osu.Game.Tests.Visual.UserInterface
var req = new GetBeatmapSetRequest(1);
api.Queue(req);
AddUntilStep("wait for api response", () => req.Result != null);
AddUntilStep("wait for api response", () => req.Response != null);
TestUpdateableBeatmapBackgroundSprite background = null;
@ -81,7 +77,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Child = background = new TestUpdateableBeatmapBackgroundSprite
{
RelativeSizeAxes = Axes.Both,
Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Result?.ToBeatmapSet(rulesets) } }
Beatmap = { Value = new APIBeatmap { BeatmapSet = req.Response } }
};
});

View File

@ -0,0 +1,181 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests.Responses;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneUpdateableBeatmapSetCover : OsuTestScene
{
[Test]
public void TestLocal([Values] BeatmapSetCoverType coverType)
{
AddStep("setup cover", () => Child = new UpdateableOnlineBeatmapSetCover(coverType)
{
OnlineInfo = CreateAPIBeatmapSet(),
RelativeSizeAxes = Axes.Both,
Masking = true,
});
AddUntilStep("wait for load", () => this.ChildrenOfType<OnlineBeatmapSetCover>().SingleOrDefault()?.IsLoaded ?? false);
}
[Test]
public void TestUnloadAndReload()
{
OsuScrollContainer scroll = null;
List<UpdateableOnlineBeatmapSetCover> covers = new List<UpdateableOnlineBeatmapSetCover>();
AddStep("setup covers", () =>
{
var beatmapSet = CreateAPIBeatmapSet();
FillFlowContainer fillFlow;
Child = scroll = new OsuScrollContainer
{
Size = new Vector2(500f),
Child = fillFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
Padding = new MarginPadding { Bottom = 550 }
}
};
var coverTypes = Enum.GetValues(typeof(BeatmapSetCoverType))
.Cast<BeatmapSetCoverType>()
.ToList();
for (int i = 0; i < 25; i++)
{
var coverType = coverTypes[i % coverTypes.Count];
var cover = new UpdateableOnlineBeatmapSetCover(coverType)
{
OnlineInfo = beatmapSet,
Height = 100,
Masking = true,
};
if (coverType == BeatmapSetCoverType.Cover)
cover.Width = 500;
else if (coverType == BeatmapSetCoverType.Card)
cover.Width = 400;
else if (coverType == BeatmapSetCoverType.List)
cover.Size = new Vector2(100, 50);
fillFlow.Add(cover);
covers.Add(cover);
}
});
var loadedCovers = covers.Where(c => c.ChildrenOfType<OnlineBeatmapSetCover>().SingleOrDefault()?.IsLoaded ?? false);
AddUntilStep("some loaded", () => loadedCovers.Any());
AddStep("scroll to end", () => scroll.ScrollToEnd());
AddUntilStep("all unloaded", () => !loadedCovers.Any());
}
[Test]
public void TestSetNullBeatmapWhileLoading()
{
TestUpdateableOnlineBeatmapSetCover updateableCover = null;
AddStep("setup cover", () => Child = updateableCover = new TestUpdateableOnlineBeatmapSetCover
{
OnlineInfo = CreateAPIBeatmapSet(),
RelativeSizeAxes = Axes.Both,
Masking = true,
});
AddStep("change model", () => updateableCover.OnlineInfo = null);
AddWaitStep("wait some", 5);
AddAssert("no cover added", () => !updateableCover.ChildrenOfType<DelayedLoadUnloadWrapper>().Any());
}
[Test]
public void TestCoverChangeOnNewBeatmap()
{
TestUpdateableOnlineBeatmapSetCover updateableCover = null;
OnlineBeatmapSetCover initialCover = null;
AddStep("setup cover", () => Child = updateableCover = new TestUpdateableOnlineBeatmapSetCover(0)
{
OnlineInfo = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg"),
RelativeSizeAxes = Axes.Both,
Masking = true,
Alpha = 0.4f
});
AddUntilStep("cover loaded", () => updateableCover.ChildrenOfType<OnlineBeatmapSetCover>().Any());
AddStep("store initial cover", () => initialCover = updateableCover.ChildrenOfType<OnlineBeatmapSetCover>().Single());
AddUntilStep("wait for fade complete", () => initialCover.Alpha == 1);
AddStep("switch beatmap",
() => updateableCover.OnlineInfo = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg"));
AddUntilStep("new cover loaded", () => updateableCover.ChildrenOfType<OnlineBeatmapSetCover>().Except(new[] { initialCover }).Any());
}
private static APIBeatmapSet createBeatmapWithCover(string coverUrl) => new APIBeatmapSet
{
Covers = new BeatmapSetOnlineCovers { Cover = coverUrl }
};
private class TestUpdateableOnlineBeatmapSetCover : UpdateableOnlineBeatmapSetCover
{
private readonly int loadDelay;
public TestUpdateableOnlineBeatmapSetCover(int loadDelay = 10000)
{
this.loadDelay = loadDelay;
}
protected override Drawable CreateDrawable(IBeatmapSetOnlineInfo model)
{
if (model == null)
return null;
return new TestOnlineBeatmapSetCover(model, loadDelay)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
};
}
}
private class TestOnlineBeatmapSetCover : OnlineBeatmapSetCover
{
private readonly int loadDelay;
public TestOnlineBeatmapSetCover(IBeatmapSetOnlineInfo set, int loadDelay)
: base(set)
{
this.loadDelay = loadDelay;
}
[BackgroundDependencyLoader]
private void load()
{
Thread.Sleep(loadDelay);
}
}
}
}

View File

@ -0,0 +1,47 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Dashboard.Friends;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneUserListToolbar : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
public TestSceneUserListToolbar()
{
UserListToolbar toolbar;
OsuSpriteText sort;
OsuSpriteText displayStyle;
Add(toolbar = new UserListToolbar
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
Add(new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
sort = new OsuSpriteText(),
displayStyle = new OsuSpriteText()
}
});
toolbar.SortCriteria.BindValueChanged(criteria => sort.Text = $"Criteria: {criteria.NewValue}", true);
toolbar.DisplayStyle.BindValueChanged(style => displayStyle.Text = $"Style: {style.NewValue}", true);
}
}
}

View File

@ -0,0 +1,32 @@
// 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 osu.Framework.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Volume;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneVolumeOverlay : OsuTestScene
{
private VolumeOverlay volume;
protected override void LoadComplete()
{
base.LoadComplete();
AddRange(new Drawable[]
{
volume = new VolumeOverlay(),
new VolumeControlReceptor
{
RelativeSizeAxes = Axes.Both,
ActionRequested = action => volume.Adjust(action),
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
},
});
AddStep("show controls", () => volume.Show());
}
}
}

View File

@ -1,8 +1,6 @@
// 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;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Overlays.Volume;
using osuTK;
@ -12,8 +10,6 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneVolumePieces : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(VolumeMeter), typeof(MuteButton) };
protected override void LoadComplete()
{
VolumeMeter meter;

View File

@ -0,0 +1,69 @@
// 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;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.UserInterface
{
public abstract class ThemeComparisonTestScene : OsuGridTestScene
{
protected ThemeComparisonTestScene()
: base(1, 2)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Cell(0, 0).AddRange(new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeaFoam
},
CreateContent()
});
}
protected void CreateThemedContent(OverlayColourScheme colourScheme)
{
var colourProvider = new OverlayColourProvider(colourScheme);
Cell(0, 1).Clear();
Cell(0, 1).Add(new DependencyProvidingContainer
{
RelativeSizeAxes = Axes.Both,
CachedDependencies = new (Type, object)[]
{
(typeof(OverlayColourProvider), colourProvider)
},
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background4
},
CreateContent()
}
});
}
protected abstract Drawable CreateContent();
[Test]
public void TestAllColourSchemes()
{
foreach (var scheme in Enum.GetValues(typeof(OverlayColourScheme)).Cast<OverlayColourScheme>())
AddStep($"set {scheme} scheme", () => CreateThemedContent(scheme));
}
}
}