diff --git a/osu.Android.props b/osu.Android.props
index 45c162a30e..4167d07698 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -62,7 +62,7 @@
-
+
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 85fd68efdd..83646c561d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -142,6 +142,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state)
{
+ base.UpdateStateTransforms(state);
+
Debug.Assert(HitObject.HitWindows != null);
switch (state)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 8a7e5117f9..c46343c73c 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -41,12 +41,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
- protected override void LoadComplete()
+ protected override void UpdateStateTransforms(ArmedState state)
{
- base.LoadComplete();
+ base.UpdateStateTransforms(state);
- // Manually set to reduce the number of future alive objects to a bare minimum.
- LifetimeStart = HitObject.StartTime - HitObject.TimePreempt;
+ switch (state)
+ {
+ case ArmedState.Idle:
+ // Manually set to reduce the number of future alive objects to a bare minimum.
+ LifetimeStart = HitObject.StartTime - HitObject.TimePreempt;
+ break;
+ }
}
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
index 00a943a67f..84d2a4af9b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
@@ -74,6 +74,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state)
{
+ base.UpdateStateTransforms(state);
+
switch (state)
{
case ArmedState.Idle:
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 65f1d5e15f..08b43b0345 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -202,6 +202,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state)
{
+ base.UpdateStateTransforms(state);
+
Ball.FadeIn();
Ball.ScaleTo(HitObject.Scale);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index ba931976a8..9d4d9958a1 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -75,6 +75,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state)
{
+ base.UpdateStateTransforms(state);
+
switch (state)
{
case ArmedState.Idle:
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index b1185ddba8..d1b9ee6cb4 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -215,6 +215,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateStateTransforms(ArmedState state)
{
+ base.UpdateStateTransforms(state);
+
var sequence = this.Delay(Spinner.Duration).FadeOut(160);
switch (state)
diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
index 5808a78056..50583e43c4 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs
@@ -160,6 +160,15 @@ namespace osu.Game.Tests.Visual.Gameplay
exitAndConfirm();
}
+ [Test]
+ public void TestRestartAfterResume()
+ {
+ pauseAndConfirm();
+ resumeAndConfirm();
+ restart();
+ confirmExited();
+ }
+
private void pauseAndConfirm()
{
pause();
@@ -198,6 +207,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("player exited", () => !Player.IsCurrentScreen());
}
+ private void restart() => AddStep("restart", () => Player.Restart());
private void pause() => AddStep("pause", () => Player.Pause());
private void resume() => AddStep("resume", () => Player.Resume());
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsDismissableFlag.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsDismissableFlag.cs
new file mode 100644
index 0000000000..db6afa9bf3
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsDismissableFlag.cs
@@ -0,0 +1,65 @@
+// Copyright (c) ppy Pty Ltd . 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.Sprites;
+using osu.Game.Graphics;
+using osu.Game.Overlays.Rankings;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsDismissableFlag : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DismissableFlag),
+ };
+
+ public TestSceneRankingsDismissableFlag()
+ {
+ DismissableFlag flag;
+ SpriteText text;
+
+ var countryA = new Country
+ {
+ FlagName = "BY",
+ FullName = "Belarus"
+ };
+
+ var countryB = new Country
+ {
+ FlagName = "US",
+ FullName = "United States"
+ };
+
+ AddRange(new Drawable[]
+ {
+ flag = new DismissableFlag
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(30, 20),
+ Country = countryA,
+ },
+ text = new SpriteText
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Text = "Invoked",
+ Font = OsuFont.GetFont(size: 30),
+ Alpha = 0,
+ }
+ });
+
+ flag.Action += () => text.FadeIn().Then().FadeOut(1000, Easing.OutQuint);
+
+ AddStep("Trigger click", () => flag.Click());
+ AddStep("Change to country 2", () => flag.Country = countryB);
+ AddStep("Change to country 1", () => flag.Country = countryA);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
new file mode 100644
index 0000000000..0ceb5f21d3
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs
@@ -0,0 +1,70 @@
+// Copyright (c) ppy Pty Ltd . 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Overlays.Rankings;
+using osu.Game.Rulesets;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsHeader : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DismissableFlag),
+ typeof(HeaderTitle),
+ typeof(RankingsRulesetSelector),
+ typeof(RankingsScopeSelector),
+ typeof(RankingsHeader),
+ };
+
+ public TestSceneRankingsHeader()
+ {
+ var countryBindable = new Bindable();
+ var ruleset = new Bindable();
+ var scope = new Bindable();
+
+ Add(new RankingsHeader
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Scope = { BindTarget = scope },
+ Country = { BindTarget = countryBindable },
+ Ruleset = { BindTarget = ruleset },
+ Spotlights = new[]
+ {
+ new Spotlight
+ {
+ Id = 1,
+ Text = "Spotlight 1"
+ },
+ new Spotlight
+ {
+ Id = 2,
+ Text = "Spotlight 2"
+ },
+ new Spotlight
+ {
+ Id = 3,
+ Text = "Spotlight 3"
+ }
+ }
+ });
+
+ var country = new Country
+ {
+ FlagName = "BY",
+ FullName = "Belarus"
+ };
+
+ AddStep("Set country", () => countryBindable.Value = country);
+ AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
+ AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
+ AddAssert("Check country is Null", () => countryBindable.Value == null);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs
new file mode 100644
index 0000000000..849ca2defc
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeaderTitle.cs
@@ -0,0 +1,60 @@
+// Copyright (c) ppy Pty Ltd . 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Overlays.Rankings;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsHeaderTitle : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(DismissableFlag),
+ typeof(HeaderTitle),
+ };
+
+ public TestSceneRankingsHeaderTitle()
+ {
+ var countryBindable = new Bindable();
+ var scope = new Bindable();
+
+ Add(new HeaderTitle
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Country = { BindTarget = countryBindable },
+ Scope = { BindTarget = scope },
+ });
+
+ var countryA = new Country
+ {
+ FlagName = "BY",
+ FullName = "Belarus"
+ };
+
+ var countryB = new Country
+ {
+ FlagName = "US",
+ FullName = "United States"
+ };
+
+ AddStep("Set country", () => countryBindable.Value = countryA);
+ AddAssert("Check scope is Performance", () => scope.Value == RankingsScope.Performance);
+ AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
+ AddAssert("Check country is Null", () => countryBindable.Value == null);
+
+ AddStep("Set country 1", () => countryBindable.Value = countryA);
+ AddStep("Set country 2", () => countryBindable.Value = countryB);
+ AddStep("Set null country", () => countryBindable.Value = null);
+ AddStep("Set scope to Performance", () => scope.Value = RankingsScope.Performance);
+ AddStep("Set scope to Spotlights", () => scope.Value = RankingsScope.Spotlights);
+ AddStep("Set scope to Score", () => scope.Value = RankingsScope.Score);
+ AddStep("Set scope to Country", () => scope.Value = RankingsScope.Country);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs
new file mode 100644
index 0000000000..3693d6b5b4
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsScopeSelector.cs
@@ -0,0 +1,54 @@
+// Copyright (c) ppy Pty Ltd . 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.Bindables;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Allocation;
+using osu.Game.Graphics;
+using osu.Game.Overlays.Rankings;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestSceneRankingsScopeSelector : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(RankingsScopeSelector),
+ };
+
+ private readonly Box background;
+
+ public TestSceneRankingsScopeSelector()
+ {
+ var scope = new Bindable();
+
+ AddRange(new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ new RankingsScopeSelector
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Current = scope,
+ }
+ });
+
+ AddStep(@"Select country", () => scope.Value = RankingsScope.Country);
+ AddStep(@"Select performance", () => scope.Value = RankingsScope.Performance);
+ AddStep(@"Select score", () => scope.Value = RankingsScope.Score);
+ AddStep(@"Select spotlights", () => scope.Value = RankingsScope.Spotlights);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ background.Colour = colours.GreySeafoam;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs
index a6ff3462d4..cc4a57fb83 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs
@@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Add(overlay = new DialogOverlay());
- AddStep("dialog #1", () => overlay.Push(new PopupDialog
+ AddStep("dialog #1", () => overlay.Push(new TestPopupDialog
{
Icon = FontAwesome.Regular.TrashAlt,
HeaderText = @"Confirm deletion of",
@@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.UserInterface
},
}));
- AddStep("dialog #2", () => overlay.Push(new PopupDialog
+ AddStep("dialog #2", () => overlay.Push(new TestPopupDialog
{
Icon = FontAwesome.Solid.Cog,
HeaderText = @"What do you want to do with",
@@ -71,5 +71,9 @@ namespace osu.Game.Tests.Visual.UserInterface
},
}));
}
+
+ private class TestPopupDialog : PopupDialog
+ {
+ }
}
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs
index 9ddd8f4038..3d39bb7003 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs
@@ -13,13 +13,22 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public TestScenePopupDialog()
{
- var popup = new PopupDialog
+ Add(new TestPopupDialog
{
RelativeSizeAxes = Axes.Both,
State = { Value = Framework.Graphics.Containers.Visibility.Visible },
- Icon = FontAwesome.Solid.AssistiveListeningSystems,
- HeaderText = @"This is a test popup",
- BodyText = "I can say lots of stuff and even wrap my words!",
+ });
+ }
+
+ private class TestPopupDialog : PopupDialog
+ {
+ public TestPopupDialog()
+ {
+ Icon = FontAwesome.Solid.AssistiveListeningSystems;
+
+ HeaderText = @"This is a test popup";
+ BodyText = "I can say lots of stuff and even wrap my words!";
+
Buttons = new PopupDialogButton[]
{
new PopupDialogCancelButton
@@ -30,10 +39,8 @@ namespace osu.Game.Tests.Visual.UserInterface
{
Text = @"You're a fake!",
},
- }
- };
-
- Add(popup);
+ };
+ }
}
}
}
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index 2e8910213b..b117d71006 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -21,8 +21,6 @@ namespace osu.Game.Graphics.Containers
private SampleChannel samplePopIn;
private SampleChannel samplePopOut;
- protected virtual bool PlaySamplesOnStateChange => true;
-
protected override bool BlockNonPositionalInput => true;
///
@@ -126,12 +124,12 @@ namespace osu.Game.Graphics.Containers
return;
}
- if (PlaySamplesOnStateChange) samplePopIn?.Play();
+ samplePopIn?.Play();
if (BlockScreenWideMouse && DimMainContent) game?.AddBlockingOverlay(this);
break;
case Visibility.Hidden:
- if (PlaySamplesOnStateChange) samplePopOut?.Play();
+ samplePopOut?.Play();
if (BlockScreenWideMouse) game?.RemoveBlockingOverlay(this);
break;
}
diff --git a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs
new file mode 100644
index 0000000000..baca57ea89
--- /dev/null
+++ b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs
@@ -0,0 +1,84 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osuTK;
+using osu.Framework.Graphics.Shapes;
+using osuTK.Graphics;
+using osu.Framework.Graphics.Colour;
+
+namespace osu.Game.Graphics.UserInterface
+{
+ public abstract class GradientLineTabControl : PageTabControl
+ {
+ protected Color4 LineColour
+ {
+ get => line.Colour;
+ set => line.Colour = value;
+ }
+
+ private readonly GradientLine line;
+
+ protected GradientLineTabControl()
+ {
+ RelativeSizeAxes = Axes.X;
+
+ AddInternal(line = new GradientLine
+ {
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ });
+ }
+
+ protected override Dropdown CreateDropdown() => null;
+
+ protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
+ {
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ AutoSizeAxes = Axes.X,
+ RelativeSizeAxes = Axes.Y,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(20, 0),
+ };
+
+ private class GradientLine : GridContainer
+ {
+ public GradientLine()
+ {
+ RelativeSizeAxes = Axes.X;
+ Size = new Vector2(0.8f, 1.5f);
+
+ ColumnDimensions = new[]
+ {
+ new Dimension(),
+ new Dimension(mode: GridSizeMode.Relative, size: 0.4f),
+ new Dimension(),
+ };
+
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Color4.White)
+ },
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.Transparent)
+ },
+ }
+ };
+ }
+ }
+ }
+}
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index 008f8208eb..0b84cfc28a 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -215,6 +215,7 @@ namespace osu.Game.Online.Leaderboards
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(1),
ChildrenEnumerable = score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
},
},
diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs
index dcd58db427..e2a725ec46 100644
--- a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs
+++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs
@@ -1,60 +1,37 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Graphics.UserInterface;
using osu.Game.Screens.Select.Leaderboards;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osuTK;
using osu.Game.Graphics.UserInterface;
-using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using osuTK.Graphics;
-using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
+using osu.Framework.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
- public class LeaderboardScopeSelector : PageTabControl
+ public class LeaderboardScopeSelector : GradientLineTabControl
{
protected override bool AddEnumEntriesAutomatically => false;
- protected override Dropdown CreateDropdown() => null;
-
protected override TabItem CreateTabItem(BeatmapLeaderboardScope value) => new ScopeSelectorTabItem(value);
public LeaderboardScopeSelector()
{
- RelativeSizeAxes = Axes.X;
-
AddItem(BeatmapLeaderboardScope.Global);
AddItem(BeatmapLeaderboardScope.Country);
AddItem(BeatmapLeaderboardScope.Friend);
-
- AddInternal(new GradientLine
- {
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- });
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AccentColour = colours.Blue;
+ LineColour = Color4.Gray;
}
- protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
- {
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- AutoSizeAxes = Axes.X,
- RelativeSizeAxes = Axes.Y,
- Direction = FillDirection.Horizontal,
- Spacing = new Vector2(20, 0),
- };
-
private class ScopeSelectorTabItem : PageTabItem
{
public ScopeSelectorTabItem(BeatmapLeaderboardScope value)
@@ -77,43 +54,5 @@ namespace osu.Game.Overlays.BeatmapSet
Text.FadeColour(Color4.White);
}
}
-
- private class GradientLine : GridContainer
- {
- public GradientLine()
- {
- RelativeSizeAxes = Axes.X;
- Size = new Vector2(0.8f, 1.5f);
-
- ColumnDimensions = new[]
- {
- new Dimension(),
- new Dimension(mode: GridSizeMode.Relative, size: 0.4f),
- new Dimension(),
- };
-
- Content = new[]
- {
- new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Color4.Gray),
- },
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Gray,
- },
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = ColourInfo.GradientHorizontal(Color4.Gray, Color4.Transparent),
- },
- }
- };
- }
- }
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
index 347522fb48..58f5f02956 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs
@@ -171,6 +171,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
+ Spacing = new Vector2(1),
ChildrenEnumerable = score.Mods.Select(m => new ModIcon(m)
{
AutoSizeAxes = Axes.Both,
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs
index 6761d0f710..b9664d7c2f 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreStatisticsSection.cs
@@ -172,7 +172,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
: this(new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(1),
})
{
}
diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs
index 1022edfe81..cff887865a 100644
--- a/osu.Game/Overlays/Dialog/PopupDialog.cs
+++ b/osu.Game/Overlays/Dialog/PopupDialog.cs
@@ -13,20 +13,17 @@ using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
-using osu.Game.Input.Bindings;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;
namespace osu.Game.Overlays.Dialog
{
- public class PopupDialog : OsuFocusedOverlayContainer
+ public abstract class PopupDialog : VisibilityContainer
{
public static readonly float ENTER_DURATION = 500;
public static readonly float EXIT_DURATION = 200;
- protected override bool BlockPositionalInput => false;
-
private readonly Vector2 ringSize = new Vector2(100f);
private readonly Vector2 ringMinifiedSize = new Vector2(20f);
private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f);
@@ -90,7 +87,7 @@ namespace osu.Game.Overlays.Dialog
}
}
- public PopupDialog()
+ protected PopupDialog()
{
RelativeSizeAxes = Axes.Both;
@@ -202,18 +199,6 @@ namespace osu.Game.Overlays.Dialog
};
}
- public override bool OnPressed(GlobalAction action)
- {
- switch (action)
- {
- case GlobalAction.Select:
- Buttons.OfType().FirstOrDefault()?.Click();
- return true;
- }
-
- return base.OnPressed(action);
- }
-
protected override bool OnKeyDown(KeyDownEvent e)
{
if (e.Repeat) return false;
@@ -238,8 +223,6 @@ namespace osu.Game.Overlays.Dialog
protected override void PopIn()
{
- base.PopIn();
-
actionInvoked = false;
// Reset various animations but only if the dialog animation fully completed
@@ -263,7 +246,6 @@ namespace osu.Game.Overlays.Dialog
// This is presumed to always be a sane default "cancel" action.
buttonsContainer.Last().Click();
- base.PopOut();
content.FadeOut(EXIT_DURATION, Easing.InSine);
}
diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs
index aaae7bcf5c..6aaeff8554 100644
--- a/osu.Game/Overlays/DialogOverlay.cs
+++ b/osu.Game/Overlays/DialogOverlay.cs
@@ -5,6 +5,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Dialog;
using osu.Game.Graphics.Containers;
+using osu.Game.Input.Bindings;
+using System.Linq;
namespace osu.Game.Overlays
{
@@ -41,8 +43,6 @@ namespace osu.Game.Overlays
Show();
}
- protected override bool PlaySamplesOnStateChange => false;
-
protected override bool BlockNonPositionalInput => true;
private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v)
@@ -74,5 +74,17 @@ namespace osu.Game.Overlays
this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine);
}
+
+ public override bool OnPressed(GlobalAction action)
+ {
+ switch (action)
+ {
+ case GlobalAction.Select:
+ currentDialog?.Buttons.OfType().FirstOrDefault()?.Click();
+ return true;
+ }
+
+ return base.OnPressed(action);
+ }
}
}
diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
index e54ce44ca2..6362d3dfb0 100644
--- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
+++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs
@@ -12,12 +12,13 @@ using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Beatmaps;
using osu.Framework.Localisation;
+using osu.Framework.Graphics.Containers;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public abstract class DrawableProfileScore : DrawableProfileRow
{
- private readonly ScoreModsContainer modsContainer;
+ private readonly FillFlowContainer modsContainer;
protected readonly ScoreInfo Score;
protected DrawableProfileScore(ScoreInfo score)
@@ -28,12 +29,12 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
Height = 60;
Children = new Drawable[]
{
- modsContainer = new ScoreModsContainer
+ modsContainer = new FillFlowContainer
{
- AutoSizeAxes = Axes.Y,
+ AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
- Width = 60,
+ Spacing = new Vector2(1),
Margin = new MarginPadding { Right = 160 }
}
};
diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs
deleted file mode 100644
index 1ce04effa8..0000000000
--- a/osu.Game/Overlays/Profile/Sections/Ranks/ScoreModsContainer.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using osuTK;
-using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.UI;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace osu.Game.Overlays.Profile.Sections.Ranks
-{
- public class ScoreModsContainer : FlowContainer
- {
- protected override IEnumerable ComputeLayoutPositions()
- {
- int count = FlowingChildren.Count();
- for (int i = 0; i < count; i++)
- yield return new Vector2(DrawWidth * i * (count == 1 ? 0 : 1f / (count - 1)), 0);
- }
- }
-}
diff --git a/osu.Game/Overlays/Rankings/DismissableFlag.cs b/osu.Game/Overlays/Rankings/DismissableFlag.cs
new file mode 100644
index 0000000000..7a55b0bba6
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/DismissableFlag.cs
@@ -0,0 +1,55 @@
+// Copyright (c) ppy Pty Ltd . 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.Users.Drawables;
+using osuTK.Graphics;
+using osuTK;
+using osu.Framework.Input.Events;
+using System;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class DismissableFlag : UpdateableFlag
+ {
+ private const int duration = 200;
+
+ public Action Action;
+
+ private readonly SpriteIcon hoverIcon;
+
+ public DismissableFlag()
+ {
+ AddInternal(hoverIcon = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Depth = -1,
+ Alpha = 0,
+ Size = new Vector2(10),
+ Icon = FontAwesome.Solid.Times,
+ });
+ }
+
+ protected override bool OnHover(HoverEvent e)
+ {
+ hoverIcon.FadeIn(duration, Easing.OutQuint);
+ this.FadeColour(Color4.Gray, duration, Easing.OutQuint);
+ return base.OnHover(e);
+ }
+
+ protected override void OnHoverLost(HoverLostEvent e)
+ {
+ base.OnHoverLost(e);
+ hoverIcon.FadeOut(duration, Easing.OutQuint);
+ this.FadeColour(Color4.White, duration, Easing.OutQuint);
+ }
+
+ protected override bool OnClick(ClickEvent e)
+ {
+ Action?.Invoke();
+ return true;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Rankings/HeaderTitle.cs b/osu.Game/Overlays/Rankings/HeaderTitle.cs
new file mode 100644
index 0000000000..cba407ecf7
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/HeaderTitle.cs
@@ -0,0 +1,98 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Bindables;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Users;
+using osu.Framework.Graphics;
+using osuTK;
+using osu.Game.Graphics;
+using osu.Framework.Allocation;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class HeaderTitle : CompositeDrawable
+ {
+ private const int spacing = 10;
+ private const int flag_margin = 5;
+ private const int text_size = 40;
+
+ public readonly Bindable Scope = new Bindable();
+ public readonly Bindable Country = new Bindable();
+
+ private readonly SpriteText scopeText;
+ private readonly DismissableFlag flag;
+
+ public HeaderTitle()
+ {
+ AutoSizeAxes = Axes.Both;
+ InternalChild = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(spacing, 0),
+ Children = new Drawable[]
+ {
+ flag = new DismissableFlag
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Margin = new MarginPadding { Bottom = flag_margin },
+ Size = new Vector2(30, 20),
+ },
+ scopeText = new SpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Light)
+ },
+ new SpriteText
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Light),
+ Text = @"Ranking"
+ }
+ }
+ };
+
+ flag.Action += () => Country.Value = null;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ scopeText.Colour = colours.Lime;
+ }
+
+ protected override void LoadComplete()
+ {
+ Scope.BindValueChanged(onScopeChanged, true);
+ Country.BindValueChanged(onCountryChanged, true);
+ base.LoadComplete();
+ }
+
+ private void onScopeChanged(ValueChangedEvent scope)
+ {
+ scopeText.Text = scope.NewValue.ToString();
+
+ if (scope.NewValue != RankingsScope.Performance)
+ Country.Value = null;
+ }
+
+ private void onCountryChanged(ValueChangedEvent country)
+ {
+ if (country.NewValue == null)
+ {
+ flag.Hide();
+ return;
+ }
+
+ Scope.Value = RankingsScope.Performance;
+
+ flag.Country = country.NewValue;
+ flag.Show();
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Rankings/RankingsHeader.cs b/osu.Game/Overlays/Rankings/RankingsHeader.cs
new file mode 100644
index 0000000000..fbf3097f4f
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/RankingsHeader.cs
@@ -0,0 +1,137 @@
+// Copyright (c) ppy Pty Ltd . 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.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Game.Rulesets;
+using osu.Game.Users;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osuTK;
+using osu.Game.Graphics.UserInterface;
+using System.Collections.Generic;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class RankingsHeader : CompositeDrawable
+ {
+ private const int content_height = 250;
+ private const int dropdown_height = 50;
+
+ public IEnumerable Spotlights
+ {
+ get => dropdown.Items;
+ set => dropdown.Items = value;
+ }
+
+ public readonly Bindable Scope = new Bindable();
+ public readonly Bindable Ruleset = new Bindable();
+ public readonly Bindable Country = new Bindable();
+ public readonly Bindable Spotlight = new Bindable();
+
+ private readonly Container dropdownPlaceholder;
+ private readonly OsuDropdown dropdown;
+
+ public RankingsHeader()
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+
+ AddInternal(new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ new RankingsRulesetSelector
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Current = Ruleset
+ },
+ new Container
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.X,
+ Height = content_height,
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Child = new HeaderBackground(),
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 20),
+ Children = new Drawable[]
+ {
+ new RankingsScopeSelector
+ {
+ Margin = new MarginPadding { Top = 10 },
+ Current = Scope
+ },
+ new HeaderTitle
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Margin = new MarginPadding { Top = 10 },
+ Scope = { BindTarget = Scope },
+ Country = { BindTarget = Country },
+ },
+ dropdownPlaceholder = new Container
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.X,
+ Height = dropdown_height,
+ Width = 0.8f,
+ AlwaysPresent = true,
+ Child = dropdown = new OsuDropdown
+ {
+ RelativeSizeAxes = Axes.X,
+ Current = Spotlight,
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ });
+ }
+
+ protected override void LoadComplete()
+ {
+ Scope.BindValueChanged(onScopeChanged, true);
+ base.LoadComplete();
+ }
+
+ private void onScopeChanged(ValueChangedEvent scope) =>
+ dropdownPlaceholder.FadeTo(scope.NewValue == RankingsScope.Spotlights ? 1 : 0, 200, Easing.OutQuint);
+
+ private class HeaderBackground : Sprite
+ {
+ public HeaderBackground()
+ {
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ RelativeSizeAxes = Axes.Both;
+ FillMode = FillMode.Fill;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ Texture = textures.Get(@"Headers/rankings");
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Rankings/RankingsScopeSelector.cs b/osu.Game/Overlays/Rankings/RankingsScopeSelector.cs
new file mode 100644
index 0000000000..2095bcc61c
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/RankingsScopeSelector.cs
@@ -0,0 +1,26 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Graphics.UserInterface;
+using osu.Framework.Allocation;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class RankingsScopeSelector : GradientLineTabControl
+ {
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ AccentColour = LineColour = Color4.Black;
+ }
+ }
+
+ public enum RankingsScope
+ {
+ Performance,
+ Spotlights,
+ Score,
+ Country
+ }
+}
diff --git a/osu.Game/Overlays/Rankings/Spotlight.cs b/osu.Game/Overlays/Rankings/Spotlight.cs
new file mode 100644
index 0000000000..e956b4f449
--- /dev/null
+++ b/osu.Game/Overlays/Rankings/Spotlight.cs
@@ -0,0 +1,18 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using Newtonsoft.Json;
+
+namespace osu.Game.Overlays.Rankings
+{
+ public class Spotlight
+ {
+ [JsonProperty("id")]
+ public int Id;
+
+ [JsonProperty("text")]
+ public string Text;
+
+ public override string ToString() => Text;
+ }
+}
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 3f1603eabe..3fd0f0260c 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -502,15 +502,18 @@ namespace osu.Game.Screens.Play
return true;
}
- if (pauseCooldownActive && !GameplayClockContainer.IsPaused.Value)
- // still want to block if we are within the cooldown period and not already paused.
- return true;
-
- if (HasFailed && ValidForResume && !FailOverlay.IsPresent)
- // ValidForResume is false when restarting
+ // ValidForResume is false when restarting
+ if (ValidForResume)
{
- failAnimation.FinishTransforms(true);
- return true;
+ if (pauseCooldownActive && !GameplayClockContainer.IsPaused.Value)
+ // still want to block if we are within the cooldown period and not already paused.
+ return true;
+
+ if (HasFailed && !FailOverlay.IsPresent)
+ {
+ failAnimation.FinishTransforms(true);
+ return true;
+ }
}
GameplayClockContainer.ResetLocalAdjustments();
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index df8b11e653..5703293caf 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -25,7 +25,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 7c31744a14..683dccf3ae 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -117,7 +117,7 @@
-
+