diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
index 0dc3f73404..625e857156 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs
@@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
index 2e18c5f2ad..b9b6d5b924 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
@@ -3,7 +3,7 @@
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osuTK.Graphics;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
index f2be8d614c..9a29273282 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
@@ -7,6 +7,7 @@ using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Scoring;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index 82a34224f4..afd7777861 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -5,7 +5,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osuTK.Graphics;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Scoring;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
index 2baf1ad520..b515abcc86 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
@@ -7,6 +7,7 @@ using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs
index b146a33fd3..1d25a0c966 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/GlowPiece.cs
@@ -4,6 +4,7 @@
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osuTK.Graphics;
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
index 89e8cd9b5a..a0d713067d 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
@@ -6,6 +6,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.UI;
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
index 03b55cbead..85880222d7 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
@@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs
index f5a9978f77..0ec1fc38d2 100644
--- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs
+++ b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs
@@ -4,6 +4,7 @@
using osuTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Mania.Objects.Drawables;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
index f3c7939a94..445f81c6d4 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs
@@ -12,6 +12,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
+using osu.Game.Scoring;
using osuTK;
using osuTK.Graphics;
@@ -41,6 +42,8 @@ namespace osu.Game.Rulesets.Osu.Mods
scoreProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); };
}
+ public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
+
///
/// Element for the Blinds mod drawing 2 black boxes covering the whole screen which resize inside a restricted area with some leniency.
///
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
index 3c64fe57d4..aacf3ee08d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs
@@ -6,6 +6,7 @@ using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Skinning;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
index 93ac8748dd..84034d3ee9 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
@@ -4,6 +4,7 @@
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Game.Graphics.Sprites;
using osuTK.Graphics;
using osu.Framework.Graphics.Shapes;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
index c982f53c2b..77228e28af 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
@@ -4,6 +4,7 @@
using osuTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs
index f47617bcf6..9219fab830 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs
@@ -5,6 +5,7 @@ using System;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Graphics.Shapes;
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
index 03dbf7ac63..4aec7c634e 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
@@ -43,22 +43,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private readonly InputResampler resampler = new InputResampler();
- protected override DrawNode CreateDrawNode() => new TrailDrawNode();
-
- protected override void ApplyDrawNode(DrawNode node)
- {
- base.ApplyDrawNode(node);
-
- TrailDrawNode tNode = (TrailDrawNode)node;
- tNode.Shader = shader;
- tNode.Texture = texture;
- tNode.Size = size;
- tNode.Time = time;
-
- for (int i = 0; i < parts.Length; ++i)
- if (parts[i].InvalidationID > tNode.Parts[i].InvalidationID)
- tNode.Parts[i] = parts[i];
- }
+ protected override DrawNode CreateDrawNode() => new TrailDrawNode(this);
public CursorTrail()
{
@@ -167,33 +152,52 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private class TrailDrawNode : DrawNode
{
- public IShader Shader;
- public Texture Texture;
+ protected new CursorTrail Source => (CursorTrail)base.Source;
- public float Time;
+ private IShader shader;
+ private Texture texture;
- public readonly TrailPart[] Parts = new TrailPart[max_sprites];
- public Vector2 Size;
+ private float time;
+
+ private readonly TrailPart[] parts = new TrailPart[max_sprites];
+ private Vector2 size;
private readonly VertexBuffer vertexBuffer = new QuadVertexBuffer(max_sprites, BufferUsageHint.DynamicDraw);
- public TrailDrawNode()
+ public TrailDrawNode(CursorTrail source)
+ : base(source)
{
for (int i = 0; i < max_sprites; i++)
{
- Parts[i].InvalidationID = 0;
- Parts[i].WasUpdated = false;
+ parts[i].InvalidationID = 0;
+ parts[i].WasUpdated = false;
+ }
+ }
+
+ public override void ApplyState()
+ {
+ base.ApplyState();
+
+ shader = Source.shader;
+ texture = Source.texture;
+ size = Source.size;
+ time = Source.time;
+
+ for (int i = 0; i < Source.parts.Length; ++i)
+ {
+ if (Source.parts[i].InvalidationID > parts[i].InvalidationID)
+ parts[i] = Source.parts[i];
}
}
public override void Draw(Action vertexAction)
{
- Shader.GetUniform("g_FadeClock").UpdateValue(ref Time);
+ shader.GetUniform("g_FadeClock").UpdateValue(ref time);
int updateStart = -1, updateEnd = 0;
- for (int i = 0; i < Parts.Length; ++i)
+ for (int i = 0; i < parts.Length; ++i)
{
- if (Parts[i].WasUpdated)
+ if (parts[i].WasUpdated)
{
if (updateStart == -1)
updateStart = i;
@@ -202,22 +206,22 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
int start = i * 4;
int end = start;
- Vector2 pos = Parts[i].Position;
- float time = Parts[i].Time;
+ Vector2 pos = parts[i].Position;
+ float localTime = parts[i].Time;
- Texture.DrawQuad(
- new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y),
+ texture.DrawQuad(
+ new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
DrawColourInfo.Colour,
null,
v => vertexBuffer.Vertices[end++] = new TexturedTrailVertex
{
Position = v.Position,
TexturePosition = v.TexturePosition,
- Time = time + 1,
+ Time = localTime + 1,
Colour = v.Colour,
});
- Parts[i].WasUpdated = false;
+ parts[i].WasUpdated = false;
}
else if (updateStart != -1)
{
@@ -232,12 +236,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
base.Draw(vertexAction);
- Shader.Bind();
+ shader.Bind();
- Texture.TextureGL.Bind();
+ texture.TextureGL.Bind();
vertexBuffer.Draw();
- Shader.Unbind();
+ shader.Unbind();
}
protected override void Dispose(bool isDisposing)
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs
index ecdafb0fa2..27546fa424 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs
@@ -6,6 +6,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
index ba7241c165..72adb4624d 100644
--- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs
@@ -1,6 +1,7 @@
// 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 System.Linq;
using osu.Framework.Input;
@@ -61,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.UI
get
{
var first = (OsuHitObject)Objects.First();
- return first.StartTime - first.TimePreempt;
+ return first.StartTime - Math.Max(2000, first.TimePreempt);
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
index 53dbe5d08e..b7db819717 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/Pieces/CirclePiece.cs
@@ -9,6 +9,7 @@ using osu.Game.Graphics.Backgrounds;
using osuTK.Graphics;
using osu.Game.Beatmaps.ControlPoints;
using osu.Framework.Audio.Track;
+using osu.Framework.Graphics.Effects;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
{
diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs
index bed2c554ec..e80b463481 100644
--- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs
+++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs
@@ -5,6 +5,7 @@ using osuTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index dbff5270d2..88d7f9a751 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
diff --git a/osu.Game.Tests/Visual/Editor/TestCaseWaveform.cs b/osu.Game.Tests/Visual/Editor/TestCaseWaveform.cs
index c35e8741c1..ce6ca08a61 100644
--- a/osu.Game.Tests/Visual/Editor/TestCaseWaveform.cs
+++ b/osu.Game.Tests/Visual/Editor/TestCaseWaveform.cs
@@ -3,12 +3,13 @@
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
+using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
-using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
@@ -16,35 +17,38 @@ namespace osu.Game.Tests.Visual.Editor
[TestFixture]
public class TestCaseWaveform : OsuTestCase
{
+ private WorkingBeatmap waveformBeatmap;
+
[BackgroundDependencyLoader]
private void load()
{
- Beatmap.Value = new WaveformTestBeatmap();
+ waveformBeatmap = new WaveformTestBeatmap();
+ }
- FillFlowContainer flow;
- Child = flow = new FillFlowContainer
+ [TestCase(1f)]
+ [TestCase(1f / 2)]
+ [TestCase(1f / 4)]
+ [TestCase(1f / 8)]
+ [TestCase(1f / 16)]
+ [TestCase(0f)]
+ public void TestResolution(float resolution)
+ {
+ TestWaveformGraph graph = null;
+
+ AddStep("add graph", () =>
{
- RelativeSizeAxes = Axes.Both,
- Direction = FillDirection.Vertical,
- Spacing = new Vector2(0, 10),
- };
-
- for (int i = 1; i <= 16; i *= 2)
- {
- var newDisplay = new WaveformGraph
- {
- RelativeSizeAxes = Axes.Both,
- Resolution = 1f / i,
- Waveform = Beatmap.Value.Waveform,
- };
-
- flow.Add(new Container
+ Child = new Container
{
RelativeSizeAxes = Axes.X,
Height = 100,
Children = new Drawable[]
{
- newDisplay,
+ graph = new TestWaveformGraph
+ {
+ RelativeSizeAxes = Axes.Both,
+ Resolution = resolution,
+ Waveform = waveformBeatmap.Waveform,
+ },
new Container
{
Anchor = Anchor.Centre,
@@ -62,13 +66,42 @@ namespace osu.Game.Tests.Visual.Editor
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Text = $"Resolution: {1f / i:0.00}"
+ Text = $"Resolution: {resolution:0.00}"
}
}
}
}
- });
- }
+ };
+ });
+
+ AddUntilStep("wait for load", () => graph.ResampledWaveform != null);
+ }
+
+ [Test]
+ public void TestDefaultBeatmap()
+ {
+ TestWaveformGraph graph = null;
+
+ AddStep("add graph", () =>
+ {
+ Child = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 100,
+ Child = graph = new TestWaveformGraph
+ {
+ RelativeSizeAxes = Axes.Both,
+ Waveform = new DummyWorkingBeatmap().Waveform,
+ },
+ };
+ });
+
+ AddUntilStep("wait for load", () => graph.ResampledWaveform != null);
+ }
+
+ public class TestWaveformGraph : WaveformGraph
+ {
+ public new Waveform ResampledWaveform => base.ResampledWaveform;
}
}
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs
index 1f0a97cc58..15c38e6d18 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs
@@ -12,6 +12,7 @@ using osu.Framework.Screens;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
@@ -96,6 +97,8 @@ namespace osu.Game.Tests.Visual.Gameplay
{
Applied = true;
}
+
+ public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
}
private class TestPlayer : Player
diff --git a/osu.Game.Tests/Visual/Gameplay/TestCaseSkinReloadable.cs b/osu.Game.Tests/Visual/Gameplay/TestCaseSkinReloadable.cs
index a9fbf35d37..56ab70b400 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestCaseSkinReloadable.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestCaseSkinReloadable.cs
@@ -120,12 +120,8 @@ namespace osu.Game.Tests.Visual.Gameplay
}
}
- private class SecondarySource : ISkinSource
+ private class SecondarySource : ISkin
{
- public event Action SourceChanged;
-
- public void TriggerSourceChanged() => SourceChanged?.Invoke();
-
public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox();
public Texture GetTexture(string componentName) => throw new NotImplementedException();
@@ -135,12 +131,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public TValue GetValue(Func query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
}
- private class SkinSourceContainer : Container, ISkinSource
+ private class SkinSourceContainer : Container, ISkin
{
- public event Action SourceChanged;
-
- public void TriggerSourceChanged() => SourceChanged?.Invoke();
-
public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox();
public Texture GetTexture(string componentName) => throw new NotImplementedException();
diff --git a/osu.Game.Tests/Visual/Online/TestCaseBadgeContainer.cs b/osu.Game.Tests/Visual/Online/TestCaseBadgeContainer.cs
deleted file mode 100644
index 631cb190d2..0000000000
--- a/osu.Game.Tests/Visual/Online/TestCaseBadgeContainer.cs
+++ /dev/null
@@ -1,62 +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 System;
-using System.Collections.Generic;
-using System.Linq;
-using NUnit.Framework;
-using osu.Framework.Graphics;
-using osu.Game.Overlays.Profile.Header;
-using osu.Game.Users;
-
-namespace osu.Game.Tests.Visual.Online
-{
- [TestFixture]
- public class TestCaseBadgeContainer : OsuTestCase
- {
- public override IReadOnlyList RequiredTypes => new[] { typeof(BadgeContainer) };
-
- public TestCaseBadgeContainer()
- {
- BadgeContainer badgeContainer;
-
- Child = badgeContainer = new BadgeContainer
- {
- RelativeSizeAxes = Axes.Both
- };
-
- AddStep("Show 1 badge", () => badgeContainer.ShowBadges(new[]
- {
- new Badge
- {
- AwardedAt = DateTimeOffset.Now,
- Description = "Appreciates compasses",
- ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png",
- }
- }));
-
- AddStep("Show 2 badges", () => badgeContainer.ShowBadges(new[]
- {
- new Badge
- {
- AwardedAt = DateTimeOffset.Now,
- Description = "Contributed to osu!lazer testing",
- ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.png",
- },
- new Badge
- {
- AwardedAt = DateTimeOffset.Now,
- Description = "Appreciates compasses",
- ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png",
- }
- }));
-
- AddStep("Show many badges", () => badgeContainer.ShowBadges(Enumerable.Range(1, 20).Select(i => new Badge
- {
- AwardedAt = DateTimeOffset.Now,
- Description = $"Contributed to osu!lazer testing {i} times",
- ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg",
- }).ToArray()));
- }
- }
-}
diff --git a/osu.Game.Tests/Visual/Online/TestCaseRankGraph.cs b/osu.Game.Tests/Visual/Online/TestCaseRankGraph.cs
index dff018bf91..a92b788e83 100644
--- a/osu.Game.Tests/Visual/Online/TestCaseRankGraph.cs
+++ b/osu.Game.Tests/Visual/Online/TestCaseRankGraph.cs
@@ -9,7 +9,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
-using osu.Game.Overlays.Profile.Header;
+using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Users;
using osuTK;
diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs b/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs
index b2877f7bd7..b015418d78 100644
--- a/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs
+++ b/osu.Game.Tests/Visual/Online/TestCaseUserPanel.cs
@@ -38,6 +38,7 @@ namespace osu.Game.Tests.Visual.Online
Country = new Country { FlagName = @"AU" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
IsSupporter = true,
+ SupportLevel = 3,
}) { Width = 300 },
},
});
diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs
index 5b86de28f9..0789c14b32 100644
--- a/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs
+++ b/osu.Game.Tests/Visual/Online/TestCaseUserProfile.cs
@@ -6,11 +6,12 @@ using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Overlays.Profile;
-using osu.Game.Overlays.Profile.Header;
+using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Online
@@ -19,7 +20,9 @@ namespace osu.Game.Tests.Visual.Online
public class TestCaseUserProfile : OsuTestCase
{
private readonly TestUserProfileOverlay profile;
- private IAPIProvider api;
+
+ [Resolved]
+ private IAPIProvider api { get; set; }
public override IReadOnlyList RequiredTypes => new[]
{
@@ -27,7 +30,46 @@ namespace osu.Game.Tests.Visual.Online
typeof(UserProfileOverlay),
typeof(RankGraph),
typeof(LineGraph),
- typeof(BadgeContainer)
+ typeof(SectionsContainer<>),
+ typeof(SupporterIcon)
+ };
+
+ public static readonly User TEST_USER = new User
+ {
+ Username = @"Somebody",
+ Id = 1,
+ Country = new Country { FullName = @"Alien" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
+ JoinDate = DateTimeOffset.Now.AddDays(-1),
+ LastVisit = DateTimeOffset.Now,
+ ProfileOrder = new[] { "me" },
+ Statistics = new UserStatistics
+ {
+ Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 },
+ PP = 4567.89m,
+ Level = new UserStatistics.LevelInfo
+ {
+ Current = 727,
+ Progress = 69,
+ }
+ },
+ RankHistory = new User.RankHistoryData
+ {
+ Mode = @"osu",
+ Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
+ },
+ Badges = new[]
+ {
+ new Badge
+ {
+ AwardedAt = DateTimeOffset.FromUnixTimeSeconds(1505741569),
+ Description = "Outstanding help by being a voluntary test subject.",
+ ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg"
+ }
+ },
+ Title = "osu!volunteer",
+ Colour = "ff0000",
+ Achievements = new User.UserAchievement[0],
};
public TestCaseUserProfile()
@@ -35,47 +77,11 @@ namespace osu.Game.Tests.Visual.Online
Add(profile = new TestUserProfileOverlay());
}
- [BackgroundDependencyLoader]
- private void load(IAPIProvider api)
- {
- this.api = api;
- }
-
protected override void LoadComplete()
{
base.LoadComplete();
- AddStep("Show offline dummy", () => profile.ShowUser(new User
- {
- Username = @"Somebody",
- Id = 1,
- Country = new Country { FullName = @"Alien" },
- CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
- JoinDate = DateTimeOffset.Now.AddDays(-1),
- LastVisit = DateTimeOffset.Now,
- ProfileOrder = new[] { "me" },
- Statistics = new UserStatistics
- {
- Ranks = new UserStatistics.UserRanks { Global = 2148, Country = 1 },
- PP = 4567.89m,
- },
- RankHistory = new User.RankHistoryData
- {
- Mode = @"osu",
- Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
- },
- Badges = new[]
- {
- new Badge
- {
- AwardedAt = DateTimeOffset.FromUnixTimeSeconds(1505741569),
- Description = "Outstanding help by being a voluntary test subject.",
- ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg"
- }
- }
- }, false));
-
- checkSupporterTag(false);
+ AddStep("Show offline dummy", () => profile.ShowUser(TEST_USER, false));
AddStep("Show null dummy", () => profile.ShowUser(new User
{
@@ -92,8 +98,6 @@ namespace osu.Game.Tests.Visual.Online
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
}, api.IsLoggedIn));
- checkSupporterTag(true);
-
AddStep("Show flyte", () => profile.ShowUser(new User
{
Username = @"flyte",
@@ -106,15 +110,6 @@ namespace osu.Game.Tests.Visual.Online
AddStep("Show without reload", profile.Show);
}
- private void checkSupporterTag(bool isSupporter)
- {
- AddUntilStep("wait for load", () => profile.Header.User != null);
- if (isSupporter)
- AddAssert("is supporter", () => profile.Header.SupporterTag.Alpha == 1);
- else
- AddAssert("no supporter", () => profile.Header.SupporterTag.Alpha == 0);
- }
-
private class TestUserProfileOverlay : UserProfileOverlay
{
public new ProfileHeader Header => base.Header;
diff --git a/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs
new file mode 100644
index 0000000000..5f5ba89186
--- /dev/null
+++ b/osu.Game.Tests/Visual/Online/TestCaseUserProfileHeader.cs
@@ -0,0 +1,81 @@
+// 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.Allocation;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Overlays.Profile;
+using osu.Game.Overlays.Profile.Header;
+using osu.Game.Overlays.Profile.Header.Components;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual.Online
+{
+ public class TestCaseUserProfileHeader : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ProfileHeader),
+ typeof(RankGraph),
+ typeof(LineGraph),
+ typeof(ProfileHeaderTabControl),
+ typeof(CentreHeaderContainer),
+ typeof(BottomHeaderContainer),
+ typeof(DetailHeaderContainer),
+ typeof(ProfileHeaderButton)
+ };
+
+ [Resolved]
+ private IAPIProvider api { get; set; }
+
+ private readonly ProfileHeader header;
+
+ public TestCaseUserProfileHeader()
+ {
+ header = new ProfileHeader();
+ Add(header);
+
+ AddStep("Show offline dummy", () => header.User.Value = TestCaseUserProfile.TEST_USER);
+
+ AddStep("Show null dummy", () => header.User.Value = new User
+ {
+ Username = "Null"
+ });
+
+ addOnlineStep("Show ppy", new User
+ {
+ Username = @"peppy",
+ Id = 2,
+ IsSupporter = true,
+ Country = new Country { FullName = @"Australia", FlagName = @"AU" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
+ });
+
+ addOnlineStep("Show flyte", new User
+ {
+ Username = @"flyte",
+ Id = 3103765,
+ Country = new Country { FullName = @"Japan", FlagName = @"JP" },
+ CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
+ });
+ }
+
+ private void addOnlineStep(string name, User fallback)
+ {
+ AddStep(name, () =>
+ {
+ if (api.IsLoggedIn)
+ {
+ var request = new GetUserRequest(fallback.Id);
+ request.Success += user => header.User.Value = user;
+ api.Queue(request);
+ }
+ else
+ header.User.Value = fallback;
+ });
+ }
+ }
+}
diff --git a/osu.Game/Audio/IPreviewTrackOwner.cs b/osu.Game/Audio/IPreviewTrackOwner.cs
index fdcae65e3c..8ab93257a5 100644
--- a/osu.Game/Audio/IPreviewTrackOwner.cs
+++ b/osu.Game/Audio/IPreviewTrackOwner.cs
@@ -4,7 +4,7 @@
namespace osu.Game.Audio
{
///
- /// Interface for objects that can own s.
+ /// Interface for objects that can own s.
///
///
/// s can cancel the currently playing through the
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index b6fa6674f6..7922843626 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -98,7 +98,7 @@ namespace osu.Game.Beatmaps
protected abstract IEnumerable ValidConversionTypes { get; }
///
- /// Creates the that will be returned by this .
+ /// Creates the that will be returned by this .
///
protected virtual Beatmap CreateBeatmap() => new Beatmap();
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 9caa64ec96..a36a8ea7dd 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -217,7 +217,7 @@ namespace osu.Game.Beatmaps
{
request.Perform(api);
}
- catch (Exception e)
+ catch
{
// no need to handle here as exceptions will filter down to request.Failure above.
}
@@ -382,7 +382,6 @@ namespace osu.Game.Beatmaps
/// Query the API to populate missing values like OnlineBeatmapID / OnlineBeatmapSetID or (Rank-)Status.
///
/// The beatmap to populate.
- /// The other beatmaps contained within this set.
/// Whether to re-query if the provided beatmap already has populated values.
/// True if population was successful.
private bool fetchAndPopulateOnlineValues(BeatmapInfo beatmap, bool force = false)
diff --git a/osu.Game/Beatmaps/BindableBeatmap.cs b/osu.Game/Beatmaps/BindableBeatmap.cs
index 657dc06297..27bad65062 100644
--- a/osu.Game/Beatmaps/BindableBeatmap.cs
+++ b/osu.Game/Beatmaps/BindableBeatmap.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps
{
///
/// A for the beatmap.
- /// This should be used sparingly in-favour of .
+ /// This should be used sparingly in-favour of .
///
public abstract class BindableBeatmap : NonNullableBindable
{
@@ -67,6 +67,6 @@ namespace osu.Game.Beatmaps
/// If you are further binding to events of the retrieved , ensure a local reference is held.
///
[NotNull]
- public abstract BindableBeatmap GetBoundCopy();
+ public new abstract BindableBeatmap GetBoundCopy();
}
}
diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
index 1be7411bec..0a0ad28fdf 100644
--- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
+++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
@@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Containers;
diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
index 58463d2219..7d25ca3ede 100644
--- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using osu.Framework.Audio.Track;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
@@ -73,9 +74,18 @@ namespace osu.Game.Beatmaps
private class DummyBeatmapConverter : IBeatmapConverter
{
public event Action> ObjectConverted;
+
public IBeatmap Beatmap { get; set; }
+
public bool CanConvert => true;
- public IBeatmap Convert() => Beatmap;
+
+ public IBeatmap Convert()
+ {
+ foreach (var obj in Beatmap.HitObjects)
+ ObjectConverted?.Invoke(obj, obj.Yield());
+
+ return Beatmap;
+ }
}
}
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 040f582e3b..31cfe076cd 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -95,7 +95,7 @@ namespace osu.Game.Beatmaps.Formats
{
colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), split.Length == 4 ? byte.Parse(split[3]) : (byte)255);
}
- catch (Exception e)
+ catch
{
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 8989785dcd..4b0720d867 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -73,6 +73,7 @@ namespace osu.Game.Beatmaps
///
///
/// The to create a playable for.
+ /// The s to apply to the .
/// The converted .
/// If could not be converted to .
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList mods)
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 3805921ac2..41f8c64853 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -563,7 +563,7 @@ namespace osu.Game.Database
///
/// Check whether an existing model already exists for a new import item.
///
- /// The new model proposed for import.
+ /// The new model proposed for import.
/// An existing model which matches the criteria to skip importing, else null.
protected TModel CheckForExisting(TModel model) => model.Hash == null ? null : ModelStore.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash);
diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs
index f6250732d9..554337c477 100644
--- a/osu.Game/Database/DatabaseContextFactory.cs
+++ b/osu.Game/Database/DatabaseContextFactory.cs
@@ -1,7 +1,6 @@
// 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.Linq;
using System.Threading;
using Microsoft.EntityFrameworkCore.Storage;
@@ -67,7 +66,7 @@ namespace osu.Game.Database
context = threadContexts.Value;
}
}
- catch (Exception e)
+ catch
{
// retrieval of a context could trigger a fatal error.
Monitor.Exit(writeLock);
diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs
index 17efe2c839..f4cd23f5a5 100644
--- a/osu.Game/Database/OsuDbContext.cs
+++ b/osu.Game/Database/OsuDbContext.cs
@@ -70,7 +70,7 @@ namespace osu.Game.Database
cmd.ExecuteNonQuery();
}
}
- catch (Exception e)
+ catch
{
connection.Close();
throw;
diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs
index c67d779c37..e2c7693700 100644
--- a/osu.Game/Graphics/Backgrounds/Triangles.cs
+++ b/osu.Game/Graphics/Backgrounds/Triangles.cs
@@ -178,64 +178,68 @@ namespace osu.Game.Graphics.Backgrounds
/// The colour.
protected virtual Color4 CreateTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1);
- protected override DrawNode CreateDrawNode() => new TrianglesDrawNode();
-
- protected override void ApplyDrawNode(DrawNode node)
- {
- base.ApplyDrawNode(node);
-
- var trianglesNode = (TrianglesDrawNode)node;
-
- trianglesNode.Shader = shader;
- trianglesNode.Texture = texture;
- trianglesNode.Size = DrawSize;
-
- trianglesNode.Parts.Clear();
- trianglesNode.Parts.AddRange(parts);
- }
+ protected override DrawNode CreateDrawNode() => new TrianglesDrawNode(this);
private class TrianglesDrawNode : DrawNode
{
- public IShader Shader;
- public Texture Texture;
+ protected new Triangles Source => (Triangles)base.Source;
- public readonly List Parts = new List();
- public Vector2 Size;
+ private IShader shader;
+ private Texture texture;
+
+ private readonly List parts = new List();
+ private Vector2 size;
private readonly LinearBatch vertexBatch = new LinearBatch(100 * 3, 10, PrimitiveType.Triangles);
+ public TrianglesDrawNode(Triangles source)
+ : base(source)
+ {
+ }
+
+ public override void ApplyState()
+ {
+ base.ApplyState();
+
+ shader = Source.shader;
+ texture = Source.texture;
+ size = Source.DrawSize;
+
+ parts.Clear();
+ parts.AddRange(Source.parts);
+ }
+
public override void Draw(Action vertexAction)
{
base.Draw(vertexAction);
- Shader.Bind();
- Texture.TextureGL.Bind();
+ shader.Bind();
+ texture.TextureGL.Bind();
Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy;
- foreach (TriangleParticle particle in Parts)
+ foreach (TriangleParticle particle in parts)
{
var offset = triangle_size * new Vector2(particle.Scale * 0.5f, particle.Scale * 0.866f);
- var size = new Vector2(2 * offset.X, offset.Y);
var triangle = new Triangle(
- Vector2Extensions.Transform(particle.Position * Size, DrawInfo.Matrix),
- Vector2Extensions.Transform(particle.Position * Size + offset, DrawInfo.Matrix),
- Vector2Extensions.Transform(particle.Position * Size + new Vector2(-offset.X, offset.Y), DrawInfo.Matrix)
+ Vector2Extensions.Transform(particle.Position * size, DrawInfo.Matrix),
+ Vector2Extensions.Transform(particle.Position * size + offset, DrawInfo.Matrix),
+ Vector2Extensions.Transform(particle.Position * size + new Vector2(-offset.X, offset.Y), DrawInfo.Matrix)
);
ColourInfo colourInfo = DrawColourInfo.Colour;
colourInfo.ApplyChild(particle.Colour);
- Texture.DrawTriangle(
+ texture.DrawTriangle(
triangle,
colourInfo,
null,
vertexBatch.AddAction,
- Vector2.Divide(localInflationAmount, size));
+ Vector2.Divide(localInflationAmount, new Vector2(2 * offset.X, offset.Y)));
}
- Shader.Unbind();
+ shader.Unbind();
}
protected override void Dispose(bool isDisposing)
diff --git a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs
index c1811f37d5..f5ef291c8f 100644
--- a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs
+++ b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs
@@ -4,6 +4,7 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osuTK;
namespace osu.Game.Graphics.Containers
diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
index eefbeea24c..222336d663 100644
--- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs
+++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
@@ -103,7 +103,7 @@ namespace osu.Game.Graphics.Containers
{
channelManager?.OpenChannel(linkArgument);
}
- catch (ChannelNotFoundException e)
+ catch (ChannelNotFoundException)
{
Logger.Log($"The requested channel \"{linkArgument}\" does not exist");
}
diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs
index fb23038dde..23015e8bf5 100644
--- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs
+++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs
@@ -30,7 +30,6 @@ namespace osu.Game.Graphics.Containers
/// Assign the logo that should track the facade's position, as well as how it should transform to its initial position.
///
/// The instance of the logo to be used for tracking.
- /// The scale of the facade. Does not actually affect the logo itself.
/// The duration of the initial transform. Default is instant.
/// The easing type of the initial transform.
public void StartTracking(OsuLogo logo, double duration = 0, Easing easing = Easing.None)
@@ -132,7 +131,7 @@ namespace osu.Game.Graphics.Containers
private class InternalFacade : Facade
{
- public void SetSize(Vector2 size)
+ public new void SetSize(Vector2 size)
{
base.SetSize(size);
}
diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs
index 880807c8b4..d5ae7cba57 100644
--- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs
@@ -35,7 +35,8 @@ namespace osu.Game.Graphics.Containers
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- HoverColour = colours.Yellow;
+ if (HoverColour == default)
+ HoverColour = colours.Yellow;
}
protected override void LoadComplete()
diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs
index 6bbab4766d..4cd248d612 100644
--- a/osu.Game/Graphics/Containers/SectionsContainer.cs
+++ b/osu.Game/Graphics/Containers/SectionsContainer.cs
@@ -142,6 +142,17 @@ namespace osu.Game.Graphics.Containers
public void ScrollToTop() => scrollContainer.ScrollTo(0);
+ public override void InvalidateFromChild(Invalidation invalidation, Drawable source = null)
+ {
+ base.InvalidateFromChild(invalidation, source);
+
+ if ((invalidation & Invalidation.DrawSize) != 0)
+ {
+ if (source == ExpandableHeader) //We need to recalculate the positions if the ExpandableHeader changed its size
+ lastKnownScroll = -1;
+ }
+ }
+
private float lastKnownScroll;
protected override void UpdateAfterChildren()
diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs
index 48131d7e86..464682a0ad 100644
--- a/osu.Game/Graphics/Containers/WaveContainer.cs
+++ b/osu.Game/Graphics/Containers/WaveContainer.cs
@@ -5,6 +5,7 @@ using System;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osuTK.Graphics;
diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs
index 4e0ce4a3e1..7bb6396041 100644
--- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs
+++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs
@@ -6,8 +6,8 @@ using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Sprites;
diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs
index 712dc4c444..9a80325ad4 100644
--- a/osu.Game/Graphics/OsuColour.cs
+++ b/osu.Game/Graphics/OsuColour.cs
@@ -92,5 +92,15 @@ namespace osu.Game.Graphics
public readonly Color4 ChatBlue = FromHex(@"17292e");
public readonly Color4 ContextMenuGray = FromHex(@"223034");
+
+ public readonly Color4 CommunityUserGreenLight = FromHex(@"deff87");
+ public readonly Color4 CommunityUserGreen = FromHex(@"05ffa2");
+ public readonly Color4 CommunityUserGreenDark = FromHex(@"a6cc00");
+ public readonly Color4 CommunityUserGrayGreenLighter = FromHex(@"9ebab1");
+ public readonly Color4 CommunityUserGrayGreenLight = FromHex(@"77998e");
+ public readonly Color4 CommunityUserGrayGreen = FromHex(@"4e7466");
+ public readonly Color4 CommunityUserGrayGreenDark = FromHex(@"33413c");
+ public readonly Color4 CommunityUserGrayGreenDarker = FromHex(@"2c3532");
+ public readonly Color4 CommunityUserGrayGreenDarkest = FromHex(@"1e2422");
}
}
diff --git a/osu.Game/Graphics/OsuFont.cs b/osu.Game/Graphics/OsuFont.cs
index c8a736f49a..5324b269ee 100644
--- a/osu.Game/Graphics/OsuFont.cs
+++ b/osu.Game/Graphics/OsuFont.cs
@@ -61,9 +61,9 @@ namespace osu.Game.Graphics
///
/// Retrieves the string representation of a .
///
- /// The .
+ /// The family string.
/// The .
- /// The string representation of in the specified .
+ /// The string representation of in the specified .
public static string GetWeightString(string family, FontWeight weight)
{
string weightString = weight.ToString();
@@ -81,6 +81,7 @@ namespace osu.Game.Graphics
///
/// Creates a new by applying adjustments to this .
///
+ /// The base .
/// The font typeface. If null, the value is copied from this .
/// The text size. If null, the value is copied from this .
/// The font weight. If null, the value is copied from this .
diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs
index dbbe5b4258..b50bf14bab 100644
--- a/osu.Game/Graphics/UserInterface/DialogButton.cs
+++ b/osu.Game/Graphics/UserInterface/DialogButton.cs
@@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics.Effects;
using osu.Game.Graphics.Containers;
using osu.Framework.Input.Events;
diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs
index 74025b71ff..10d1854ef6 100644
--- a/osu.Game/Graphics/UserInterface/LineGraph.cs
+++ b/osu.Game/Graphics/UserInterface/LineGraph.cs
@@ -9,6 +9,7 @@ using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
+using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface
{
@@ -63,6 +64,12 @@ namespace osu.Game.Graphics.UserInterface
}
}
+ public Color4 LineColour
+ {
+ get => maskingContainer.Colour;
+ set => maskingContainer.Colour = value;
+ }
+
public LineGraph()
{
Add(maskingContainer = new Container
diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs
index 1f5195eaf1..82b09e0821 100644
--- a/osu.Game/Graphics/UserInterface/Nub.cs
+++ b/osu.Game/Graphics/UserInterface/Nub.cs
@@ -8,6 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
index d64068f74c..a8041c79fc 100644
--- a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
@@ -5,6 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
diff --git a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs
index c72d11b57e..cea8427296 100644
--- a/osu.Game/Graphics/UserInterface/OsuContextMenu.cs
+++ b/osu.Game/Graphics/UserInterface/OsuContextMenu.cs
@@ -5,7 +5,7 @@ using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
namespace osu.Game.Graphics.UserInterface
{
diff --git a/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs
index f564a4b5a8..3e0a6c3265 100644
--- a/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs
+++ b/osu.Game/Graphics/UserInterface/ScreenBreadcrumbControl.cs
@@ -8,7 +8,7 @@ using osu.Framework.Screens;
namespace osu.Game.Graphics.UserInterface
{
///
- /// A which follows the active screen (and allows navigation) in a stack.
+ /// A which follows the active screen (and allows navigation) in a stack.
///
public class ScreenBreadcrumbControl : BreadcrumbControl
{
diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs
index 1574023068..b9d9b5427d 100644
--- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs
+++ b/osu.Game/Graphics/UserInterface/ScreenTitle.cs
@@ -14,6 +14,8 @@ namespace osu.Game.Graphics.UserInterface
{
private readonly SpriteIcon iconSprite;
private readonly OsuSpriteText titleText, pageText;
+ public const float ICON_WIDTH = icon_size + icon_spacing;
+ private const float icon_size = 25, icon_spacing = 10;
protected IconUsage Icon
{
@@ -48,12 +50,12 @@ namespace osu.Game.Graphics.UserInterface
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
- Spacing = new Vector2(10, 0),
+ Spacing = new Vector2(icon_spacing, 0),
Children = new Drawable[]
{
iconSprite = new SpriteIcon
{
- Size = new Vector2(25),
+ Size = new Vector2(icon_size),
},
new FillFlowContainer
{
diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
index 9911a7c368..36a9aca412 100644
--- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
+++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
@@ -12,6 +12,7 @@ using osu.Game.Graphics.Containers;
using osu.Game.Beatmaps.ControlPoints;
using osu.Framework.Audio.Track;
using System;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs
index c5f6ef41c2..d5a496dc17 100644
--- a/osu.Game/Online/API/APIAccess.cs
+++ b/osu.Game/Online/API/APIAccess.cs
@@ -253,7 +253,7 @@ namespace osu.Game.Online.API
handleWebException(we);
return false;
}
- catch (Exception e)
+ catch
{
return false;
}
diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs
index 8ee71ce9ac..ca3a77a140 100644
--- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs
+++ b/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs
@@ -7,7 +7,6 @@ using System.Linq;
using Newtonsoft.Json;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
-using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring.Legacy;
using osu.Game.Users;
@@ -71,7 +70,6 @@ namespace osu.Game.Online.API.Requests.Responses
{
foreach (var kvp in value)
{
- HitResult newKey;
switch (kvp.Key)
{
case @"count_geki":
diff --git a/osu.Game/Online/API/Requests/Responses/APIMod.cs b/osu.Game/Online/API/Requests/Responses/APIMod.cs
index d7dda07b33..b9da4f49ee 100644
--- a/osu.Game/Online/API/Requests/Responses/APIMod.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIMod.cs
@@ -8,5 +8,7 @@ namespace osu.Game.Online.API.Requests.Responses
public class APIMod : IMod
{
public string Acronym { get; set; }
+
+ public bool Equals(IMod other) => Acronym == other?.Acronym;
}
}
diff --git a/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs
index 8177f99abe..4614fe29b7 100644
--- a/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs
@@ -10,16 +10,16 @@ namespace osu.Game.Online.API.Requests.Responses
public class APIUserMostPlayedBeatmap
{
[JsonProperty("beatmap_id")]
- public int BeatmapID;
+ public int BeatmapID { get; set; }
[JsonProperty("count")]
- public int PlayCount;
+ public int PlayCount { get; set; }
[JsonProperty]
- private BeatmapInfo beatmap;
+ private BeatmapInfo beatmap { get; set; }
[JsonProperty]
- private APIBeatmapSet beatmapSet;
+ private APIBeatmapSet beatmapSet { get; set; }
public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets)
{
diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs
index 438bf231c4..ae4a056033 100644
--- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs
+++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs
@@ -27,8 +27,6 @@ namespace osu.Game.Online.Chat
protected ChannelManager ChannelManager;
- private ScrollContainer scroll;
-
private DrawableChannel drawableChannel;
private readonly bool postingTextbox;
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index 70edcc3fc8..c6db939f6b 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -8,6 +8,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 30f98aa1ce..4ce056195c 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -272,7 +272,6 @@ namespace osu.Game
/// Present a score's replay immediately.
/// The user should have already requested this interactively.
///
- /// The beatmap to select.
public void PresentScore(ScoreInfo score)
{
var databasedScore = ScoreManager.GetScore(score);
diff --git a/osu.Game/Overlays/AccountCreationOverlay.cs b/osu.Game/Overlays/AccountCreationOverlay.cs
index e8e44c206e..0d376257e0 100644
--- a/osu.Game/Overlays/AccountCreationOverlay.cs
+++ b/osu.Game/Overlays/AccountCreationOverlay.cs
@@ -5,6 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
index 18de87e7b4..abe954aa80 100644
--- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
+++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
@@ -11,6 +11,7 @@ using osuTK;
using osuTK.Graphics;
using osu.Game.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs
index 95cf9e9d04..3659769752 100644
--- a/osu.Game/Overlays/BeatmapSet/Header.cs
+++ b/osu.Game/Overlays/BeatmapSet/Header.cs
@@ -6,6 +6,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs
index 4d974a0b63..44827f0a0c 100644
--- a/osu.Game/Overlays/BeatmapSet/Info.cs
+++ b/osu.Game/Overlays/BeatmapSet/Info.cs
@@ -6,6 +6,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
index d8bd15a4a9..8e806c6747 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs
@@ -5,6 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs
index c0d9ecad3a..e70bf4c572 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs
@@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs
index c49268bc16..82bac71f5e 100644
--- a/osu.Game/Overlays/BeatmapSetOverlay.cs
+++ b/osu.Game/Overlays/BeatmapSetOverlay.cs
@@ -7,6 +7,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps;
diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs
index 908ec5f026..66a6672ab1 100644
--- a/osu.Game/Overlays/Chat/ChatLine.cs
+++ b/osu.Game/Overlays/Chat/ChatLine.cs
@@ -8,6 +8,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs
index 52260506fe..c26ecfd86f 100644
--- a/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs
+++ b/osu.Game/Overlays/Chat/Tabs/ChannelSelectorTabItem.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Chat.Tabs
}
[BackgroundDependencyLoader]
- private new void load(OsuColour colour)
+ private void load(OsuColour colour)
{
BackgroundInactive = colour.Gray2;
BackgroundActive = colour.Gray3;
diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs
index a4aefa4c4f..7f820e4ff7 100644
--- a/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs
+++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabItem.cs
@@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
diff --git a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs
index 8aa6d6fecd..b8165e70cb 100644
--- a/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs
+++ b/osu.Game/Overlays/Chat/Tabs/PrivateChannelTabItem.cs
@@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
using osu.Game.Users;
using osuTK;
@@ -18,9 +17,6 @@ namespace osu.Game.Overlays.Chat.Tabs
{
public class PrivateChannelTabItem : ChannelTabItem
{
- private readonly OsuSpriteText username;
- private readonly Avatar avatarContainer;
-
protected override IconUsage DisplayIcon => FontAwesome.Solid.At;
public PrivateChannelTabItem(Channel value)
diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs
index 91f42a491a..dddb3a9fc8 100644
--- a/osu.Game/Overlays/Dialog/PopupDialog.cs
+++ b/osu.Game/Overlays/Dialog/PopupDialog.cs
@@ -6,6 +6,7 @@ using System.Linq;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs
index 2b509f370e..f413dc3771 100644
--- a/osu.Game/Overlays/Direct/DirectPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectPanel.cs
@@ -8,6 +8,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
diff --git a/osu.Game/Overlays/HoldToConfirmOverlay.cs b/osu.Game/Overlays/HoldToConfirmOverlay.cs
index 154aff605a..fb38ddcbd1 100644
--- a/osu.Game/Overlays/HoldToConfirmOverlay.cs
+++ b/osu.Game/Overlays/HoldToConfirmOverlay.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Overlays
{
///
/// An overlay which will display a black screen that dims over a period before confirming an exit action.
- /// Action is BYO (derived class will need to call and from a user event).
+ /// Action is BYO (derived class will need to call and from a user event).
///
public abstract class HoldToConfirmOverlay : HoldToConfirmContainer
{
diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
index 8313dac50a..58d6cd10d2 100644
--- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
+++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
@@ -8,6 +8,7 @@ using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
diff --git a/osu.Game/Overlays/MedalOverlay.cs b/osu.Game/Overlays/MedalOverlay.cs
index a5703eba92..6d82db5603 100644
--- a/osu.Game/Overlays/MedalOverlay.cs
+++ b/osu.Game/Overlays/MedalOverlay.cs
@@ -19,6 +19,7 @@ using osu.Framework.Graphics.Textures;
using osuTK.Input;
using osu.Framework.Graphics.Shapes;
using System;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Input.Events;
using osu.Framework.MathUtils;
diff --git a/osu.Game/Overlays/Music/CollectionsDropdown.cs b/osu.Game/Overlays/Music/CollectionsDropdown.cs
index aa93e349e8..4f59b053b6 100644
--- a/osu.Game/Overlays/Music/CollectionsDropdown.cs
+++ b/osu.Game/Overlays/Music/CollectionsDropdown.cs
@@ -6,7 +6,7 @@ using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs
index 8cbea63fe3..949090e8b8 100644
--- a/osu.Game/Overlays/Music/PlaylistOverlay.cs
+++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs
@@ -8,6 +8,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index c250d3b62a..b03700190f 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -10,6 +10,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
@@ -50,7 +51,6 @@ namespace osu.Game.Overlays
private BeatmapManager beatmaps;
private List beatmapSets;
- private BeatmapSetInfo currentSet;
private Container dragContainer;
private Container playerContainer;
diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs
index 522e039cdb..2dc6b39a92 100644
--- a/osu.Game/Overlays/Notifications/Notification.cs
+++ b/osu.Game/Overlays/Notifications/Notification.cs
@@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Game.Graphics;
using osuTK;
using osuTK.Graphics;
diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs
index 5e45fbf081..9198455bf7 100644
--- a/osu.Game/Overlays/OnScreenDisplay.cs
+++ b/osu.Game/Overlays/OnScreenDisplay.cs
@@ -14,6 +14,7 @@ using osu.Game.Graphics;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Threading;
using osu.Game.Configuration;
diff --git a/osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs b/osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs
deleted file mode 100644
index 93f08768f7..0000000000
--- a/osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs
+++ /dev/null
@@ -1,20 +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 System;
-using osu.Game.Graphics;
-
-namespace osu.Game.Overlays.Profile.Components
-{
- public class DrawableJoinDate : DrawableDate
- {
- public DrawableJoinDate(DateTimeOffset date)
- : base(date)
- {
- }
-
- protected override string Format() => Text = Date.ToUniversalTime().Year < 2008 ? "Here since the beginning" : $"{Date:MMMM yyyy}";
-
- public override string TooltipText => $"{Date:MMMM d, yyyy}";
- }
-}
diff --git a/osu.Game/Overlays/Profile/Components/GradeBadge.cs b/osu.Game/Overlays/Profile/Components/GradeBadge.cs
deleted file mode 100644
index ca56780663..0000000000
--- a/osu.Game/Overlays/Profile/Components/GradeBadge.cs
+++ /dev/null
@@ -1,50 +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 osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
-using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
-
-namespace osu.Game.Overlays.Profile.Components
-{
- public class GradeBadge : Container
- {
- private const float width = 50;
- private readonly string grade;
- private readonly Sprite badge;
- private readonly SpriteText numberText;
-
- public int DisplayCount
- {
- set => numberText.Text = value.ToString(@"#,0");
- }
-
- public GradeBadge(string grade)
- {
- this.grade = grade;
- Width = width;
- Height = 41;
- Add(badge = new Sprite
- {
- Width = width,
- Height = 26
- });
- Add(numberText = new OsuSpriteText
- {
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold)
- });
- }
-
- [BackgroundDependencyLoader]
- private void load(TextureStore textures)
- {
- badge.Texture = textures.Get($"Grades/{grade}");
- }
- }
-}
diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs
deleted file mode 100644
index ff4d7a10dc..0000000000
--- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs
+++ /dev/null
@@ -1,197 +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 System;
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Cursor;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
-using osu.Framework.Input.Events;
-using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
-using osu.Game.Users;
-using osuTK;
-
-namespace osu.Game.Overlays.Profile.Header
-{
- public class BadgeContainer : Container
- {
- private static readonly Vector2 badge_size = new Vector2(86, 40);
- private static readonly MarginPadding outer_padding = new MarginPadding(3);
-
- private OsuSpriteText badgeCountText;
- private FillFlowContainer badgeFlowContainer;
- private FillFlowContainer outerBadgeContainer;
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- Child = new Container
- {
- Masking = true,
- CornerRadius = 4,
- AutoSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = colours.Gray3
- },
- outerBadgeContainer = new OuterBadgeContainer(onOuterHover, onOuterHoverLost)
- {
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- Direction = FillDirection.Vertical,
- Padding = outer_padding,
- Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal,
- AutoSizeAxes = Axes.Y,
- Children = new Drawable[]
- {
- badgeCountText = new OsuSpriteText
- {
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- Alpha = 0,
- Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular)
- },
- new Container
- {
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- AutoSizeAxes = Axes.Both,
- Child = badgeFlowContainer = new FillFlowContainer
- {
- Direction = FillDirection.Horizontal,
- AutoSizeAxes = Axes.Both,
- }
- }
- }
- },
- }
- };
-
- Scheduler.AddDelayed(rotateBadges, 3000, true);
- }
-
- private void rotateBadges()
- {
- if (outerBadgeContainer.IsHovered) return;
-
- visibleBadge = (visibleBadge + 1) % badgeCount;
-
- badgeFlowContainer.MoveToX(-DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge, 500, Easing.InOutQuad);
- }
-
- private int visibleBadge;
- private int badgeCount;
-
- public void ShowBadges(Badge[] badges)
- {
- if (badges == null || badges.Length == 0)
- {
- Hide();
- return;
- }
-
- badgeCount = badges.Length;
-
- badgeCountText.FadeTo(badgeCount > 1 ? 1 : 0);
- badgeCountText.Text = $"{badges.Length} badges";
-
- Show();
- visibleBadge = 0;
-
- badgeFlowContainer.Clear();
- for (var index = 0; index < badges.Length; index++)
- {
- int displayIndex = index;
- LoadComponentAsync(new DrawableBadge(badges[index])
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- }, asyncBadge =>
- {
- badgeFlowContainer.Add(asyncBadge);
-
- // load in stable order regardless of async load order.
- badgeFlowContainer.SetLayoutPosition(asyncBadge, displayIndex);
- });
- }
- }
-
- private void onOuterHover()
- {
- badgeFlowContainer.ClearTransforms();
- badgeFlowContainer.X = 0;
- badgeFlowContainer.Direction = FillDirection.Full;
- outerBadgeContainer.AutoSizeAxes = Axes.Both;
-
- badgeFlowContainer.MaximumSize = new Vector2(ChildSize.X, float.MaxValue);
- }
-
- private void onOuterHoverLost()
- {
- badgeFlowContainer.X = -DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge;
- badgeFlowContainer.Direction = FillDirection.Horizontal;
- outerBadgeContainer.AutoSizeAxes = Axes.Y;
- outerBadgeContainer.Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal;
- }
-
- private class OuterBadgeContainer : FillFlowContainer
- {
- private readonly Action hoverAction;
- private readonly Action hoverLostAction;
-
- public OuterBadgeContainer(Action hoverAction, Action hoverLostAction)
- {
- this.hoverAction = hoverAction;
- this.hoverLostAction = hoverLostAction;
- }
-
- protected override bool OnHover(HoverEvent e)
- {
- hoverAction();
- return true;
- }
-
- protected override void OnHoverLost(HoverLostEvent e) => hoverLostAction();
- }
-
- private class DrawableBadge : Container, IHasTooltip
- {
- public static readonly Vector2 DRAWABLE_BADGE_SIZE = badge_size + outer_padding.Total;
-
- private readonly Badge badge;
-
- public DrawableBadge(Badge badge)
- {
- this.badge = badge;
- Padding = outer_padding;
- Size = DRAWABLE_BADGE_SIZE;
- }
-
- [BackgroundDependencyLoader]
- private void load(LargeTextureStore textures)
- {
- Child = new Sprite
- {
- FillMode = FillMode.Fit,
- RelativeSizeAxes = Axes.Both,
- Texture = textures.Get(badge.ImageUrl),
- };
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- Child.FadeInFromZero(200);
- }
-
- public string TooltipText => badge.Description;
- }
- }
-}
diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs
new file mode 100644
index 0000000000..f97fecb913
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs
@@ -0,0 +1,153 @@
+// 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.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osu.Game.Users;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Profile.Header
+{
+ public class BottomHeaderContainer : CompositeDrawable
+ {
+ public readonly Bindable User = new Bindable();
+
+ private LinkFlowContainer topLinkContainer;
+ private LinkFlowContainer bottomLinkContainer;
+
+ private Color4 iconColour;
+
+ public BottomHeaderContainer()
+ {
+ AutoSizeAxes = Axes.Y;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ iconColour = colours.CommunityUserGrayGreenLighter;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.CommunityUserGrayGreenDarker,
+ },
+ new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 },
+ Spacing = new Vector2(0, 10),
+ Children = new Drawable[]
+ {
+ topLinkContainer = new LinkFlowContainer(text => text.Font = text.Font.With(size: 12))
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ },
+ bottomLinkContainer = new LinkFlowContainer(text => text.Font = text.Font.With(size: 12))
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ }
+ }
+ }
+ };
+
+ User.BindValueChanged(user => updateDisplay(user.NewValue));
+ }
+
+ private void updateDisplay(User user)
+ {
+ topLinkContainer.Clear();
+ bottomLinkContainer.Clear();
+
+ if (user == null) return;
+
+ if (user.JoinDate.ToUniversalTime().Year < 2008)
+ topLinkContainer.AddText("Here since the beginning");
+ else
+ {
+ topLinkContainer.AddText("Joined ");
+ topLinkContainer.AddText(new DrawableDate(user.JoinDate), embolden);
+ }
+
+ addSpacer(topLinkContainer);
+
+ if (user.PlayStyles?.Length > 0)
+ {
+ topLinkContainer.AddText("Plays with ");
+ topLinkContainer.AddText(string.Join(", ", user.PlayStyles.Select(style => style.GetDescription())), embolden);
+
+ addSpacer(topLinkContainer);
+ }
+
+ if (user.LastVisit.HasValue)
+ {
+ topLinkContainer.AddText("Last seen ");
+ topLinkContainer.AddText(new DrawableDate(user.LastVisit.Value), embolden);
+
+ addSpacer(topLinkContainer);
+ }
+
+ topLinkContainer.AddText("Contributed ");
+ topLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: embolden);
+
+ string websiteWithoutProtcol = user.Website;
+ if (!string.IsNullOrEmpty(websiteWithoutProtcol))
+ {
+ if (Uri.TryCreate(websiteWithoutProtcol, UriKind.Absolute, out var uri))
+ {
+ websiteWithoutProtcol = uri.Host + uri.PathAndQuery + uri.Fragment;
+ websiteWithoutProtcol = websiteWithoutProtcol.TrimEnd('/');
+ }
+ }
+
+ tryAddInfo(FontAwesome.Solid.MapMarker, user.Location);
+ tryAddInfo(OsuIcon.Heart, user.Interests);
+ tryAddInfo(FontAwesome.Solid.Suitcase, user.Occupation);
+ bottomLinkContainer.NewLine();
+ if (!string.IsNullOrEmpty(user.Twitter))
+ tryAddInfo(FontAwesome.Brands.Twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
+ tryAddInfo(FontAwesome.Brands.Discord, user.Discord);
+ tryAddInfo(FontAwesome.Brands.Skype, user.Skype, @"skype:" + user.Skype + @"?chat");
+ tryAddInfo(FontAwesome.Brands.Lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}");
+ tryAddInfo(FontAwesome.Solid.Link, websiteWithoutProtcol, user.Website);
+ }
+
+ private void addSpacer(OsuTextFlowContainer textFlow) => textFlow.AddArbitraryDrawable(new Container { Width = 15 });
+
+ private void tryAddInfo(IconUsage icon, string content, string link = null)
+ {
+ if (string.IsNullOrEmpty(content)) return;
+
+ bottomLinkContainer.AddIcon(icon, text =>
+ {
+ text.Font = text.Font.With(size: 10);
+ text.Colour = iconColour;
+ });
+
+ if (link != null)
+ bottomLinkContainer.AddLink(" " + content, link, creationParameters: embolden);
+ else
+ bottomLinkContainer.AddText(" " + content, embolden);
+
+ addSpacer(bottomLinkContainer);
+ }
+
+ private void embolden(SpriteText text) => text.Font = text.Font.With(weight: FontWeight.Bold);
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs
new file mode 100644
index 0000000000..0642ef94df
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/CentreHeaderContainer.cs
@@ -0,0 +1,150 @@
+// 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.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Textures;
+using osu.Game.Graphics;
+using osu.Game.Overlays.Profile.Header.Components;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Overlays.Profile.Header
+{
+ public class CentreHeaderContainer : CompositeDrawable
+ {
+ public readonly BindableBool DetailsVisible = new BindableBool(true);
+ public readonly Bindable User = new Bindable();
+
+ private OverlinedInfoContainer hiddenDetailGlobal;
+ private OverlinedInfoContainer hiddenDetailCountry;
+
+ public CentreHeaderContainer()
+ {
+ Height = 60;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, TextureStore textures)
+ {
+ Container hiddenDetailContainer;
+ Container expandedDetailContainer;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.CommunityUserGrayGreenDark
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.X,
+ RelativeSizeAxes = Axes.Y,
+ Direction = FillDirection.Horizontal,
+ Padding = new MarginPadding { Vertical = 10 },
+ Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN },
+ Spacing = new Vector2(10, 0),
+ Children = new Drawable[]
+ {
+ new AddFriendButton
+ {
+ RelativeSizeAxes = Axes.Y,
+ User = { BindTarget = User }
+ },
+ new MessageUserButton
+ {
+ User = { BindTarget = User }
+ },
+ }
+ },
+ new Container
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ RelativeSizeAxes = Axes.Y,
+ Padding = new MarginPadding { Vertical = 10 },
+ Width = UserProfileOverlay.CONTENT_X_MARGIN,
+ Child = new ExpandDetailsButton
+ {
+ RelativeSizeAxes = Axes.Y,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ DetailsVisible = { BindTarget = DetailsVisible }
+ },
+ },
+ new Container
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ AutoSizeAxes = Axes.Both,
+ Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN },
+ Children = new Drawable[]
+ {
+ new LevelBadge
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Size = new Vector2(40),
+ User = { BindTarget = User }
+ },
+ expandedDetailContainer = new Container
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Width = 200,
+ Height = 6,
+ Margin = new MarginPadding { Right = 50 },
+ Child = new LevelProgressBar
+ {
+ RelativeSizeAxes = Axes.Both,
+ User = { BindTarget = User }
+ }
+ },
+ hiddenDetailContainer = new FillFlowContainer
+ {
+ Direction = FillDirection.Horizontal,
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Width = 200,
+ AutoSizeAxes = Axes.Y,
+ Alpha = 0,
+ Spacing = new Vector2(10, 0),
+ Margin = new MarginPadding { Right = 50 },
+ Children = new[]
+ {
+ hiddenDetailGlobal = new OverlinedInfoContainer
+ {
+ Title = "Global Ranking",
+ LineColour = colours.Yellow
+ },
+ hiddenDetailCountry = new OverlinedInfoContainer
+ {
+ Title = "Country Ranking",
+ LineColour = colours.Yellow
+ },
+ }
+ }
+ }
+ }
+ };
+
+ DetailsVisible.BindValueChanged(visible =>
+ {
+ hiddenDetailContainer.Alpha = visible.NewValue ? 0 : 1;
+ expandedDetailContainer.Alpha = visible.NewValue ? 1 : 0;
+ }, true);
+
+ User.BindValueChanged(user => updateDisplay(user.NewValue));
+ }
+
+ private void updateDisplay(User user)
+ {
+ hiddenDetailGlobal.Content = user?.Statistics?.Ranks.Global?.ToString("\\##,##0") ?? "-";
+ hiddenDetailCountry.Content = user?.Statistics?.Ranks.Country?.ToString("\\##,##0") ?? "-";
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs
new file mode 100644
index 0000000000..2e4fd6fe3d
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/AddFriendButton.cs
@@ -0,0 +1,58 @@
+// 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.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public class AddFriendButton : ProfileHeaderButton
+ {
+ public readonly Bindable User = new Bindable();
+
+ public override string TooltipText => "friends";
+
+ private OsuSpriteText followerText;
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Child = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Direction = FillDirection.Horizontal,
+ Padding = new MarginPadding { Right = 10 },
+ Children = new Drawable[]
+ {
+ new SpriteIcon
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Icon = FontAwesome.Solid.User,
+ FillMode = FillMode.Fit,
+ Size = new Vector2(50, 14)
+ },
+ followerText = new OsuSpriteText
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Font = OsuFont.GetFont(weight: FontWeight.Bold)
+ }
+ }
+ };
+
+ User.BindValueChanged(user => updateFollowers(user.NewValue), true);
+ }
+
+ private void updateFollowers(User user) => followerText.Text = user?.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0";
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/DrawableBadge.cs b/osu.Game/Overlays/Profile/Header/Components/DrawableBadge.cs
new file mode 100644
index 0000000000..ea259fe49a
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/DrawableBadge.cs
@@ -0,0 +1,46 @@
+// 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.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public class DrawableBadge : CompositeDrawable, IHasTooltip
+ {
+ public static readonly Vector2 DRAWABLE_BADGE_SIZE = new Vector2(86, 40);
+
+ private readonly Badge badge;
+
+ public DrawableBadge(Badge badge)
+ {
+ this.badge = badge;
+ Size = DRAWABLE_BADGE_SIZE;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(LargeTextureStore textures)
+ {
+ InternalChild = new Sprite
+ {
+ FillMode = FillMode.Fit,
+ RelativeSizeAxes = Axes.Both,
+ Texture = textures.Get(badge.ImageUrl),
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ InternalChild.FadeInFromZero(200);
+ }
+
+ public string TooltipText => badge.Description;
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs
new file mode 100644
index 0000000000..089228b2cd
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/ExpandDetailsButton.cs
@@ -0,0 +1,45 @@
+// 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.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
+using osuTK;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public class ExpandDetailsButton : ProfileHeaderButton
+ {
+ public readonly BindableBool DetailsVisible = new BindableBool();
+
+ public override string TooltipText => DetailsVisible.Value ? "collapse" : "expand";
+
+ private SpriteIcon icon;
+
+ public ExpandDetailsButton()
+ {
+ Action = () => DetailsVisible.Toggle();
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ IdleColour = colours.CommunityUserGrayGreen;
+ HoverColour = colours.CommunityUserGrayGreen.Darken(0.2f);
+
+ Child = icon = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(20, 12)
+ };
+
+ DetailsVisible.BindValueChanged(visible => updateState(visible.NewValue), true);
+ }
+
+ private void updateState(bool detailsVisible) => icon.Icon = detailsVisible ? FontAwesome.Solid.ChevronUp : FontAwesome.Solid.ChevronDown;
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs
new file mode 100644
index 0000000000..8069937810
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/LevelBadge.cs
@@ -0,0 +1,57 @@
+// 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.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Users;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public class LevelBadge : CompositeDrawable, IHasTooltip
+ {
+ public readonly Bindable User = new Bindable();
+
+ public string TooltipText { get; }
+
+ private OsuSpriteText levelText;
+
+ public LevelBadge()
+ {
+ TooltipText = "Level";
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, TextureStore textures)
+ {
+ InternalChildren = new Drawable[]
+ {
+ new Sprite
+ {
+ RelativeSizeAxes = Axes.Both,
+ Texture = textures.Get("Profile/levelbadge"),
+ Colour = colours.Yellow,
+ },
+ levelText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Font = OsuFont.GetFont(size: 20)
+ }
+ };
+
+ User.BindValueChanged(user => updateLevel(user.NewValue));
+ }
+
+ private void updateLevel(User user)
+ {
+ levelText.Text = user?.Statistics?.Level.Current.ToString() ?? "0";
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.cs
new file mode 100644
index 0000000000..6a6532764f
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/LevelProgressBar.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 osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Users;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public class LevelProgressBar : CompositeDrawable, IHasTooltip
+ {
+ public readonly Bindable User = new Bindable();
+
+ public string TooltipText { get; }
+
+ private Bar levelProgressBar;
+ private OsuSpriteText levelProgressText;
+
+ public LevelProgressBar()
+ {
+ TooltipText = "Progress to next level";
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ InternalChildren = new Drawable[]
+ {
+ new CircularContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Child = levelProgressBar = new Bar
+ {
+ RelativeSizeAxes = Axes.Both,
+ BackgroundColour = Color4.Black,
+ Direction = BarDirection.LeftToRight,
+ AccentColour = colours.Yellow
+ }
+ },
+ levelProgressText = new OsuSpriteText
+ {
+ Anchor = Anchor.BottomRight,
+ Origin = Anchor.TopRight,
+ Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold)
+ }
+ };
+
+ User.BindValueChanged(user => updateProgress(user.NewValue));
+ }
+
+ private void updateProgress(User user)
+ {
+ levelProgressBar.Length = user?.Statistics?.Level.Progress / 100f ?? 0;
+ levelProgressText.Text = user?.Statistics?.Level.Progress.ToString("0'%'");
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs
new file mode 100644
index 0000000000..cc6edcdd6a
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/MessageUserButton.cs
@@ -0,0 +1,59 @@
+// 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.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Online.API;
+using osu.Game.Online.Chat;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public class MessageUserButton : ProfileHeaderButton
+ {
+ public readonly Bindable User = new Bindable();
+
+ public override string TooltipText => "send message";
+
+ [Resolved(CanBeNull = true)]
+ private ChannelManager channelManager { get; set; }
+
+ [Resolved(CanBeNull = true)]
+ private UserProfileOverlay userOverlay { get; set; }
+
+ [Resolved(CanBeNull = true)]
+ private ChatOverlay chatOverlay { get; set; }
+
+ [Resolved]
+ private IAPIProvider apiProvider { get; set; }
+
+ public MessageUserButton()
+ {
+ Content.Alpha = 0;
+ RelativeSizeAxes = Axes.Y;
+
+ Child = new SpriteIcon
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Icon = FontAwesome.Solid.Envelope,
+ FillMode = FillMode.Fit,
+ Size = new Vector2(50, 14)
+ };
+
+ Action = () =>
+ {
+ if (!Content.IsPresent) return;
+
+ channelManager?.OpenPrivateChannel(User.Value);
+ userOverlay?.Hide();
+ chatOverlay?.Show();
+ };
+
+ User.ValueChanged += e => Content.Alpha = !e.NewValue.PMFriendsOnly && apiProvider.LocalUser.Value.Id != e.NewValue.Id ? 1 : 0;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs
new file mode 100644
index 0000000000..c40ddca688
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedInfoContainer.cs
@@ -0,0 +1,64 @@
+// 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.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public class OverlinedInfoContainer : CompositeDrawable
+ {
+ private readonly Circle line;
+ private readonly OsuSpriteText title;
+ private readonly OsuSpriteText content;
+
+ public string Title
+ {
+ set => title.Text = value;
+ }
+
+ public string Content
+ {
+ set => content.Text = value;
+ }
+
+ public Color4 LineColour
+ {
+ set => line.Colour = value;
+ }
+
+ public OverlinedInfoContainer(bool big = false, int minimumWidth = 60)
+ {
+ AutoSizeAxes = Axes.Both;
+ InternalChild = new FillFlowContainer
+ {
+ Direction = FillDirection.Vertical,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ line = new Circle
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 4,
+ },
+ title = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: big ? 14 : 12, weight: FontWeight.Bold)
+ },
+ content = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: big ? 40 : 18, weight: FontWeight.Light)
+ },
+ new Container //Add a minimum size to the FillFlowContainer
+ {
+ Width = minimumWidth,
+ }
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs b/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs
new file mode 100644
index 0000000000..2c88a83680
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/OverlinedTotalPlayTime.cs
@@ -0,0 +1,69 @@
+// 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.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Game.Graphics;
+using osu.Game.Users;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public class OverlinedTotalPlayTime : CompositeDrawable, IHasTooltip
+ {
+ public readonly Bindable User = new Bindable();
+
+ public string TooltipText { get; set; }
+
+ private OverlinedInfoContainer info;
+
+ public OverlinedTotalPlayTime()
+ {
+ AutoSizeAxes = Axes.Both;
+
+ TooltipText = "0 hours";
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ InternalChild = info = new OverlinedInfoContainer
+ {
+ Title = "Total Play Time",
+ LineColour = colours.Yellow,
+ };
+
+ User.BindValueChanged(updateTime, true);
+ }
+
+ private void updateTime(ValueChangedEvent user)
+ {
+ TooltipText = (user.NewValue?.Statistics?.PlayTime ?? 0) / 3600 + " hours";
+ info.Content = formatTime(user.NewValue?.Statistics?.PlayTime);
+ }
+
+ private string formatTime(int? secondsNull)
+ {
+ if (secondsNull == null) return "0h 0m";
+
+ int seconds = secondsNull.Value;
+ string time = "";
+
+ int days = seconds / 86400;
+ seconds -= days * 86400;
+ if (days > 0)
+ time += days + "d ";
+
+ int hours = seconds / 3600;
+ seconds -= hours * 3600;
+ time += hours + "h ";
+
+ int minutes = seconds / 60;
+ time += minutes + "m";
+
+ return time;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.cs
new file mode 100644
index 0000000000..1650f11523
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/ProfileHeaderButton.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.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public abstract class ProfileHeaderButton : OsuHoverContainer, IHasTooltip
+ {
+ public abstract string TooltipText { get; }
+
+ private readonly Box background;
+ private readonly Container content;
+
+ protected override Container Content => content;
+
+ protected override IEnumerable EffectTargets => new[] { background };
+
+ protected ProfileHeaderButton()
+ {
+ AutoSizeAxes = Axes.X;
+
+ IdleColour = Color4.Black;
+ HoverColour = OsuColour.Gray(0.1f);
+
+ base.Content.Add(new CircularContainer
+ {
+ Masking = true,
+ AutoSizeAxes = Axes.X,
+ RelativeSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ content = new Container
+ {
+ AutoSizeAxes = Axes.X,
+ RelativeSizeAxes = Axes.Y,
+ Padding = new MarginPadding { Horizontal = 10 },
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs
new file mode 100644
index 0000000000..1dabf167e3
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs
@@ -0,0 +1,280 @@
+// 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 System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Events;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public class RankGraph : Container, IHasCustomTooltip
+ {
+ private const float secondary_textsize = 13;
+ private const float padding = 10;
+ private const float fade_duration = 150;
+ private const int ranked_days = 88;
+
+ private readonly RankChartLineGraph graph;
+ private readonly OsuSpriteText placeholder;
+
+ private KeyValuePair[] ranks;
+ private int dayIndex;
+ public Bindable User = new Bindable();
+
+ public RankGraph()
+ {
+ Padding = new MarginPadding { Vertical = padding };
+ Children = new Drawable[]
+ {
+ placeholder = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Text = "No recent plays",
+ Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular)
+ },
+ graph = new RankChartLineGraph
+ {
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ RelativeSizeAxes = Axes.Both,
+ Y = -secondary_textsize,
+ Alpha = 0,
+ }
+ };
+
+ graph.OnBallMove += i => dayIndex = i;
+
+ User.ValueChanged += userChanged;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ graph.LineColour = colours.Yellow;
+ }
+
+ private void userChanged(ValueChangedEvent e)
+ {
+ placeholder.FadeIn(fade_duration, Easing.Out);
+
+ if (e.NewValue?.Statistics?.Ranks.Global == null)
+ {
+ graph.FadeOut(fade_duration, Easing.Out);
+ ranks = null;
+ return;
+ }
+
+ int[] userRanks = e.NewValue.RankHistory?.Data ?? new[] { e.NewValue.Statistics.Ranks.Global.Value };
+ ranks = userRanks.Select((x, index) => new KeyValuePair(index, x)).Where(x => x.Value != 0).ToArray();
+
+ if (ranks.Length > 1)
+ {
+ placeholder.FadeOut(fade_duration, Easing.Out);
+
+ graph.DefaultValueCount = ranks.Length;
+ graph.Values = ranks.Select(x => -(float)Math.Log(x.Value));
+ }
+
+ graph.FadeTo(ranks.Length > 1 ? 1 : 0, fade_duration, Easing.Out);
+ }
+
+ protected override bool OnHover(HoverEvent e)
+ {
+ if (ranks?.Length > 1)
+ {
+ graph.UpdateBallPosition(e.MousePosition.X);
+ graph.ShowBall();
+ }
+
+ return base.OnHover(e);
+ }
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ if (ranks?.Length > 1)
+ graph.UpdateBallPosition(e.MousePosition.X);
+
+ return base.OnMouseMove(e);
+ }
+
+ protected override void OnHoverLost(HoverLostEvent e)
+ {
+ if (ranks?.Length > 1)
+ {
+ graph.HideBall();
+ }
+
+ base.OnHoverLost(e);
+ }
+
+ private class RankChartLineGraph : LineGraph
+ {
+ private readonly CircularContainer movingBall;
+ private readonly Box ballBg;
+ private readonly Box movingBar;
+
+ public Action OnBallMove;
+
+ public RankChartLineGraph()
+ {
+ Add(movingBar = new Box
+ {
+ Origin = Anchor.TopCentre,
+ RelativeSizeAxes = Axes.Y,
+ Width = 1.5f,
+ Alpha = 0,
+ RelativePositionAxes = Axes.Both,
+ });
+
+ Add(movingBall = new CircularContainer
+ {
+ Origin = Anchor.Centre,
+ Size = new Vector2(18),
+ Alpha = 0,
+ Masking = true,
+ BorderThickness = 4,
+ RelativePositionAxes = Axes.Both,
+ Child = ballBg = new Box { RelativeSizeAxes = Axes.Both }
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ ballBg.Colour = colours.CommunityUserGrayGreenDarkest;
+ movingBall.BorderColour = colours.Yellow;
+ movingBar.Colour = colours.Yellow;
+ }
+
+ public void UpdateBallPosition(float mouseXPosition)
+ {
+ int index = calculateIndex(mouseXPosition);
+ movingBall.Position = calculateBallPosition(index);
+ movingBar.X = movingBall.X;
+ OnBallMove.Invoke(index);
+ }
+
+ public void ShowBall()
+ {
+ movingBall.FadeIn(fade_duration);
+ movingBar.FadeIn(fade_duration);
+ }
+
+ public void HideBall()
+ {
+ movingBall.FadeOut(fade_duration);
+ movingBar.FadeOut(fade_duration);
+ }
+
+ private int calculateIndex(float mouseXPosition) => (int)Math.Round(mouseXPosition / DrawWidth * (DefaultValueCount - 1));
+
+ private Vector2 calculateBallPosition(int index)
+ {
+ float y = GetYPosition(Values.ElementAt(index));
+ return new Vector2(index / (float)(DefaultValueCount - 1), y);
+ }
+ }
+
+ public string TooltipText => User.Value?.Statistics?.Ranks.Global == null ? "" : $"#{ranks[dayIndex].Value:#,##0}|{ranked_days - ranks[dayIndex].Key + 1}";
+
+ public ITooltip GetCustomTooltip() => new RankGraphTooltip();
+
+ public class RankGraphTooltip : VisibilityContainer, ITooltip
+ {
+ private readonly OsuSpriteText globalRankingText, timeText;
+ private readonly Box background;
+
+ public string TooltipText { get; set; }
+
+ public RankGraphTooltip()
+ {
+ AutoSizeAxes = Axes.Both;
+ Masking = true;
+ CornerRadius = 10;
+
+ Children = new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Padding = new MarginPadding(10),
+ Children = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
+ Text = "Global Ranking "
+ },
+ globalRankingText = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular),
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ }
+ }
+ },
+ timeText = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular),
+ }
+ }
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ background.Colour = colours.CommunityUserGrayGreenDarker;
+ }
+
+ public void Refresh()
+ {
+ var info = TooltipText.Split('|');
+ globalRankingText.Text = info[0];
+ timeText.Text = info[1] == "0" ? "now" : $"{info[1]} days ago";
+ }
+
+ private bool instantMove = true;
+
+ public void Move(Vector2 pos)
+ {
+ if (instantMove)
+ {
+ Position = pos;
+ instantMove = false;
+ }
+ else
+ this.MoveTo(pos, 200, Easing.OutQuint);
+ }
+
+ protected override void PopIn() => this.FadeIn(200, Easing.OutQuint);
+
+ protected override void PopOut() => this.FadeOut(200, Easing.OutQuint);
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs
new file mode 100644
index 0000000000..cb12a62702
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/Components/SupporterIcon.cs
@@ -0,0 +1,85 @@
+// 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.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
+using osuTK;
+
+namespace osu.Game.Overlays.Profile.Header.Components
+{
+ public class SupporterIcon : CompositeDrawable, IHasTooltip
+ {
+ private readonly Box background;
+ private readonly FillFlowContainer iconContainer;
+ private readonly CircularContainer content;
+
+ public string TooltipText => "osu!supporter";
+
+ public int SupportLevel
+ {
+ set
+ {
+ int count = MathHelper.Clamp(value, 0, 3);
+
+ if (count == 0)
+ {
+ content.Hide();
+ }
+ else
+ {
+ content.Show();
+ iconContainer.Clear();
+ for (int i = 0; i < count; i++)
+ {
+ iconContainer.Add(new SpriteIcon
+ {
+ Width = 12,
+ RelativeSizeAxes = Axes.Y,
+ Icon = FontAwesome.Solid.Heart,
+ });
+ }
+
+ iconContainer.Padding = new MarginPadding { Horizontal = DrawHeight / 2 };
+ }
+ }
+ }
+
+ public SupporterIcon()
+ {
+ AutoSizeAxes = Axes.X;
+
+ InternalChild = content = new CircularContainer
+ {
+ RelativeSizeAxes = Axes.Y,
+ AutoSizeAxes = Axes.X,
+ Masking = true,
+ Alpha = 0,
+ Children = new Drawable[]
+ {
+ background = new Box { RelativeSizeAxes = Axes.Both },
+ iconContainer = new FillFlowContainer
+ {
+ Direction = FillDirection.Horizontal,
+ RelativeSizeAxes = Axes.Y,
+ AutoSizeAxes = Axes.X,
+ Height = 0.6f,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre
+ }
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ background.Colour = colours.Pink;
+ iconContainer.Colour = colours.CommunityUserGrayGreenDark;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs
new file mode 100644
index 0000000000..8fcf2711dd
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/DetailHeaderContainer.cs
@@ -0,0 +1,202 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Overlays.Profile.Header.Components;
+using osu.Game.Scoring;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Overlays.Profile.Header
+{
+ public class DetailHeaderContainer : CompositeDrawable
+ {
+ private readonly Dictionary scoreRankInfos = new Dictionary();
+ private OverlinedInfoContainer medalInfo;
+ private OverlinedInfoContainer ppInfo;
+ private OverlinedInfoContainer detailGlobalRank;
+ private OverlinedInfoContainer detailCountryRank;
+ private RankGraph rankGraph;
+
+ public readonly Bindable User = new Bindable();
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ AutoSizeAxes = Axes.Y;
+ User.ValueChanged += e => updateDisplay(e.NewValue);
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.CommunityUserGrayGreenDarkest,
+ },
+ new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 },
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(0, 20),
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(10, 0),
+ Children = new Drawable[]
+ {
+ new OverlinedTotalPlayTime
+ {
+ User = { BindTarget = User }
+ },
+ medalInfo = new OverlinedInfoContainer
+ {
+ Title = "Medals",
+ LineColour = colours.GreenLight,
+ },
+ ppInfo = new OverlinedInfoContainer
+ {
+ Title = "pp",
+ LineColour = colours.Red,
+ },
+ }
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Direction = FillDirection.Horizontal,
+ Children = new[]
+ {
+ scoreRankInfos[ScoreRank.XH] = new ScoreRankInfo(ScoreRank.XH),
+ scoreRankInfos[ScoreRank.X] = new ScoreRankInfo(ScoreRank.X),
+ scoreRankInfos[ScoreRank.SH] = new ScoreRankInfo(ScoreRank.SH),
+ scoreRankInfos[ScoreRank.S] = new ScoreRankInfo(ScoreRank.S),
+ scoreRankInfos[ScoreRank.A] = new ScoreRankInfo(ScoreRank.A),
+ }
+ }
+ }
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Padding = new MarginPadding { Right = 130 },
+ Children = new Drawable[]
+ {
+ rankGraph = new RankGraph
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ Width = 130,
+ Anchor = Anchor.TopRight,
+ Direction = FillDirection.Vertical,
+ Padding = new MarginPadding { Horizontal = 10 },
+ Spacing = new Vector2(0, 20),
+ Children = new Drawable[]
+ {
+ detailGlobalRank = new OverlinedInfoContainer(true, 110)
+ {
+ Title = "Global Ranking",
+ LineColour = colours.Yellow,
+ },
+ detailCountryRank = new OverlinedInfoContainer(false, 110)
+ {
+ Title = "Country Ranking",
+ LineColour = colours.Yellow,
+ },
+ }
+ }
+ }
+ },
+ }
+ },
+ };
+ }
+
+ private void updateDisplay(User user)
+ {
+ medalInfo.Content = user?.Achievements?.Length.ToString() ?? "0";
+ ppInfo.Content = user?.Statistics?.PP?.ToString("#,##0") ?? "0";
+
+ foreach (var scoreRankInfo in scoreRankInfos)
+ scoreRankInfo.Value.RankCount = user?.Statistics?.GradesCount[scoreRankInfo.Key] ?? 0;
+
+ detailGlobalRank.Content = user?.Statistics?.Ranks.Global?.ToString("\\##,##0") ?? "-";
+ detailCountryRank.Content = user?.Statistics?.Ranks.Country?.ToString("\\##,##0") ?? "-";
+
+ rankGraph.User.Value = user;
+ }
+
+ private class ScoreRankInfo : CompositeDrawable
+ {
+ private readonly ScoreRank rank;
+ private readonly Sprite rankSprite;
+ private readonly OsuSpriteText rankCount;
+
+ public int RankCount
+ {
+ set => rankCount.Text = value.ToString("#,##0");
+ }
+
+ public ScoreRankInfo(ScoreRank rank)
+ {
+ this.rank = rank;
+
+ AutoSizeAxes = Axes.Both;
+ InternalChild = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ Width = 56,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ rankSprite = new Sprite
+ {
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit
+ },
+ rankCount = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre
+ }
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ rankSprite.Texture = textures.Get($"Grades/{rank.GetDescription()}");
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs
new file mode 100644
index 0000000000..1e214b2d0c
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs
@@ -0,0 +1,92 @@
+// 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.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Overlays.Profile.Header.Components;
+using osu.Game.Users;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Profile.Header
+{
+ public class MedalHeaderContainer : CompositeDrawable
+ {
+ private FillFlowContainer badgeFlowContainer;
+
+ public readonly Bindable User = new Bindable();
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Alpha = 0;
+ AutoSizeAxes = Axes.Y;
+ User.ValueChanged += e => updateDisplay(e.NewValue);
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.CommunityUserGrayGreenDarkest,
+ },
+ new Container //artificial shadow
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 3,
+ Child = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = new ColourInfo
+ {
+ TopLeft = Color4.Black.Opacity(0.2f),
+ TopRight = Color4.Black.Opacity(0.2f),
+ BottomLeft = Color4.Black.Opacity(0),
+ BottomRight = Color4.Black.Opacity(0)
+ }
+ },
+ },
+ badgeFlowContainer = new FillFlowContainer
+ {
+ Direction = FillDirection.Full,
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Margin = new MarginPadding { Top = 5 },
+ Spacing = new Vector2(10, 10),
+ Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 },
+ }
+ };
+ }
+
+ private void updateDisplay(User user)
+ {
+ var badges = user.Badges;
+ badgeFlowContainer.Clear();
+ if (badges?.Length > 0)
+ {
+ Show();
+ for (var index = 0; index < badges.Length; index++)
+ {
+ int displayIndex = index;
+ LoadComponentAsync(new DrawableBadge(badges[index]), asyncBadge =>
+ {
+ badgeFlowContainer.Add(asyncBadge);
+
+ // load in stable order regardless of async load order.
+ badgeFlowContainer.SetLayoutPosition(asyncBadge, displayIndex);
+ });
+ }
+ }
+ else
+ {
+ Hide();
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs
new file mode 100644
index 0000000000..3b16b102d5
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/ProfileHeaderTabControl.cs
@@ -0,0 +1,155 @@
+// 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.Shapes;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Input.Events;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Game.Overlays.Profile.Header
+{
+ public class ProfileHeaderTabControl : TabControl
+ {
+ private readonly Box bar;
+
+ private Color4 accentColour;
+
+ public Color4 AccentColour
+ {
+ get => accentColour;
+ set
+ {
+ if (accentColour == value)
+ return;
+
+ accentColour = value;
+ bar.Colour = value;
+
+ foreach (TabItem tabItem in TabContainer)
+ {
+ ((ProfileHeaderTabItem)tabItem).AccentColour = value;
+ }
+ }
+ }
+
+ public new MarginPadding Padding
+ {
+ get => TabContainer.Padding;
+ set => TabContainer.Padding = value;
+ }
+
+ public ProfileHeaderTabControl()
+ {
+ TabContainer.Masking = false;
+ TabContainer.Spacing = new Vector2(15, 0);
+
+ AddInternal(bar = new Box
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 2,
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.CentreLeft
+ });
+ }
+
+ protected override Dropdown CreateDropdown() => null;
+
+ protected override TabItem CreateTabItem(string value) => new ProfileHeaderTabItem(value)
+ {
+ AccentColour = AccentColour
+ };
+
+ private class ProfileHeaderTabItem : TabItem
+ {
+ private readonly OsuSpriteText text;
+ private readonly Drawable bar;
+
+ private Color4 accentColour;
+
+ public Color4 AccentColour
+ {
+ get => accentColour;
+ set
+ {
+ if (accentColour == value)
+ return;
+
+ accentColour = value;
+ bar.Colour = value;
+
+ updateState();
+ }
+ }
+
+ public ProfileHeaderTabItem(string value)
+ : base(value)
+ {
+ AutoSizeAxes = Axes.X;
+ RelativeSizeAxes = Axes.Y;
+
+ Children = new[]
+ {
+ text = new OsuSpriteText
+ {
+ Margin = new MarginPadding { Bottom = 10 },
+ Origin = Anchor.BottomLeft,
+ Anchor = Anchor.BottomLeft,
+ Text = value,
+ Font = OsuFont.GetFont()
+ },
+ bar = new Circle
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 0,
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.BottomLeft,
+ },
+ new HoverClickSounds()
+ };
+ }
+
+ protected override bool OnHover(HoverEvent e)
+ {
+ base.OnHover(e);
+
+ updateState();
+
+ return true;
+ }
+
+ protected override void OnHoverLost(HoverLostEvent e)
+ {
+ base.OnHoverLost(e);
+
+ updateState();
+ }
+
+ protected override void OnActivated() => updateState();
+
+ protected override void OnDeactivated() => updateState();
+
+ private void updateState()
+ {
+ if (Active.Value || IsHovered)
+ {
+ text.FadeColour(Color4.White, 120, Easing.InQuad);
+ bar.ResizeHeightTo(7.5f, 120, Easing.InQuad);
+
+ if (Active.Value)
+ text.Font = text.Font.With(weight: FontWeight.Bold);
+ }
+ else
+ {
+ text.FadeColour(AccentColour, 120, Easing.InQuad);
+ bar.ResizeHeightTo(0, 120, Easing.InQuad);
+ text.Font = text.Font.With(weight: FontWeight.Medium);
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/Header/RankGraph.cs b/osu.Game/Overlays/Profile/Header/RankGraph.cs
deleted file mode 100644
index 3df0677576..0000000000
--- a/osu.Game/Overlays/Profile/Header/RankGraph.cs
+++ /dev/null
@@ -1,216 +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 System;
-using System.Collections.Generic;
-using System.Linq;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Input.Events;
-using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
-using osu.Game.Graphics.UserInterface;
-using osu.Game.Users;
-using osuTK;
-
-namespace osu.Game.Overlays.Profile.Header
-{
- public class RankGraph : Container
- {
- private const float primary_textsize = 25;
- private const float secondary_textsize = 13;
- private const float padding = 10;
- private const float fade_duration = 150;
- private const int ranked_days = 88;
-
- private readonly SpriteText rankText, performanceText, relativeText;
- private readonly RankChartLineGraph graph;
- private readonly OsuSpriteText placeholder;
-
- private KeyValuePair[] ranks;
- public Bindable User = new Bindable();
-
- public RankGraph()
- {
- Padding = new MarginPadding { Vertical = padding };
- Children = new Drawable[]
- {
- placeholder = new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Text = "No recent plays",
- Font = OsuFont.GetFont(size: 14, weight: FontWeight.Regular, italics: true)
- },
- rankText = new OsuSpriteText
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Font = OsuFont.GetFont(size: primary_textsize, weight: FontWeight.Regular, italics: true),
- },
- relativeText = new OsuSpriteText
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Font = OsuFont.GetFont(size: secondary_textsize, weight: FontWeight.Regular, italics: true),
- Y = 25,
- },
- performanceText = new OsuSpriteText
- {
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- Font = OsuFont.GetFont(size: secondary_textsize, weight: FontWeight.Regular, italics: true)
- },
- graph = new RankChartLineGraph
- {
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- RelativeSizeAxes = Axes.X,
- Height = 60,
- Y = -secondary_textsize,
- Alpha = 0,
- }
- };
-
- graph.OnBallMove += showHistoryRankTexts;
-
- User.ValueChanged += userChanged;
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- graph.Colour = colours.Yellow;
- }
-
- private void userChanged(ValueChangedEvent e)
- {
- placeholder.FadeIn(fade_duration, Easing.Out);
-
- if (e.NewValue?.Statistics?.Ranks.Global == null)
- {
- rankText.Text = string.Empty;
- performanceText.Text = string.Empty;
- relativeText.Text = string.Empty;
- graph.FadeOut(fade_duration, Easing.Out);
- ranks = null;
- return;
- }
-
- int[] userRanks = e.NewValue.RankHistory?.Data ?? new[] { e.NewValue.Statistics.Ranks.Global.Value };
- ranks = userRanks.Select((x, index) => new KeyValuePair(index, x)).Where(x => x.Value != 0).ToArray();
-
- if (ranks.Length > 1)
- {
- placeholder.FadeOut(fade_duration, Easing.Out);
-
- graph.DefaultValueCount = ranks.Length;
- graph.Values = ranks.Select(x => -(float)Math.Log(x.Value));
- graph.SetStaticBallPosition();
- }
-
- graph.FadeTo(ranks.Length > 1 ? 1 : 0, fade_duration, Easing.Out);
-
- updateRankTexts();
- }
-
- private void updateRankTexts()
- {
- var user = User.Value;
-
- performanceText.Text = user.Statistics.PP != null ? $"{user.Statistics.PP:#,0}pp" : string.Empty;
- rankText.Text = user.Statistics.Ranks.Global > 0 ? $"#{user.Statistics.Ranks.Global:#,0}" : "no rank";
- relativeText.Text = user.Country != null && user.Statistics.Ranks.Country > 0 ? $"{user.Country.FullName} #{user.Statistics.Ranks.Country:#,0}" : "no rank";
- }
-
- private void showHistoryRankTexts(int dayIndex)
- {
- rankText.Text = $"#{ranks[dayIndex].Value:#,0}";
- relativeText.Text = dayIndex + 1 == ranks.Length ? "Now" : $"{ranked_days - ranks[dayIndex].Key} days ago";
- }
-
- protected override bool OnHover(HoverEvent e)
- {
- if (ranks?.Length > 1)
- {
- graph.UpdateBallPosition(e.MousePosition.X);
- graph.ShowBall();
- }
-
- return base.OnHover(e);
- }
-
- protected override bool OnMouseMove(MouseMoveEvent e)
- {
- if (ranks?.Length > 1)
- graph.UpdateBallPosition(e.MousePosition.X);
-
- return base.OnMouseMove(e);
- }
-
- protected override void OnHoverLost(HoverLostEvent e)
- {
- if (ranks?.Length > 1)
- {
- graph.HideBall();
- updateRankTexts();
- }
-
- base.OnHoverLost(e);
- }
-
- private class RankChartLineGraph : LineGraph
- {
- private readonly CircularContainer staticBall;
- private readonly CircularContainer movingBall;
-
- public Action OnBallMove;
-
- public RankChartLineGraph()
- {
- Add(staticBall = new CircularContainer
- {
- Origin = Anchor.Centre,
- Size = new Vector2(8),
- Masking = true,
- RelativePositionAxes = Axes.Both,
- Child = new Box { RelativeSizeAxes = Axes.Both }
- });
- Add(movingBall = new CircularContainer
- {
- Origin = Anchor.Centre,
- Size = new Vector2(8),
- Alpha = 0,
- Masking = true,
- RelativePositionAxes = Axes.Both,
- Child = new Box { RelativeSizeAxes = Axes.Both }
- });
- }
-
- public void SetStaticBallPosition() => staticBall.Position = new Vector2(1, GetYPosition(Values.Last()));
-
- public void UpdateBallPosition(float mouseXPosition)
- {
- int index = calculateIndex(mouseXPosition);
- movingBall.Position = calculateBallPosition(index);
- OnBallMove.Invoke(index);
- }
-
- public void ShowBall() => movingBall.FadeIn(fade_duration);
-
- public void HideBall() => movingBall.FadeOut(fade_duration);
-
- private int calculateIndex(float mouseXPosition) => (int)Math.Round(mouseXPosition / DrawWidth * (DefaultValueCount - 1));
-
- private Vector2 calculateBallPosition(int index)
- {
- float y = GetYPosition(Values.ElementAt(index));
- return new Vector2(index / (float)(DefaultValueCount - 1), y);
- }
- }
- }
-}
diff --git a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs
deleted file mode 100644
index 5c9126dbe0..0000000000
--- a/osu.Game/Overlays/Profile/Header/SupporterIcon.cs
+++ /dev/null
@@ -1,65 +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 osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Cursor;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-using osu.Game.Graphics;
-using osu.Game.Graphics.Backgrounds;
-using osuTK;
-
-namespace osu.Game.Overlays.Profile.Header
-{
- public class SupporterIcon : CircularContainer, IHasTooltip
- {
- private readonly Box background;
-
- public string TooltipText => "osu!supporter";
-
- public SupporterIcon()
- {
- Masking = true;
- Children = new Drawable[]
- {
- new Box { RelativeSizeAxes = Axes.Both },
- new CircularContainer
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- Scale = new Vector2(0.8f),
- Masking = true,
- Children = new Drawable[]
- {
- background = new Box { RelativeSizeAxes = Axes.Both },
- new Triangles
- {
- TriangleScale = 0.2f,
- ColourLight = OsuColour.FromHex(@"ff7db7"),
- ColourDark = OsuColour.FromHex(@"de5b95"),
- RelativeSizeAxes = Axes.Both,
- Velocity = 0.3f,
- },
- }
- },
- new SpriteIcon
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- Icon = FontAwesome.Solid.Heart,
- Scale = new Vector2(0.45f),
- }
- };
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- background.Colour = colours.Pink;
- }
- }
-}
diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs
new file mode 100644
index 0000000000..c1fe430bdd
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs
@@ -0,0 +1,201 @@
+// 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 osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays.Profile.Header.Components;
+using osu.Game.Users;
+using osuTK;
+
+namespace osu.Game.Overlays.Profile.Header
+{
+ public class TopHeaderContainer : CompositeDrawable
+ {
+ private const float avatar_size = 110;
+
+ public readonly Bindable User = new Bindable();
+
+ private SupporterIcon supporterTag;
+ private UpdateableAvatar avatar;
+ private OsuSpriteText usernameText;
+ private ExternalLinkButton openUserExternally;
+ private OsuSpriteText titleText;
+ private DrawableFlag userFlag;
+ private OsuSpriteText userCountryText;
+ private FillFlowContainer userStats;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Height = 150;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.CommunityUserGrayGreenDarker,
+ },
+ new FillFlowContainer
+ {
+ Direction = FillDirection.Horizontal,
+ Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN },
+ Height = avatar_size,
+ AutoSizeAxes = Axes.X,
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Children = new[]
+ {
+ avatar = new UpdateableAvatar
+ {
+ Size = new Vector2(avatar_size),
+ Masking = true,
+ CornerRadius = avatar_size * 0.25f,
+ OpenOnClick = { Value = false },
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Y,
+ AutoSizeAxes = Axes.X,
+ Padding = new MarginPadding { Left = 10 },
+ Children = new Drawable[]
+ {
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ usernameText = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: 24, weight: FontWeight.Regular)
+ },
+ openUserExternally = new ExternalLinkButton
+ {
+ Margin = new MarginPadding { Left = 5 },
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ },
+ }
+ },
+ new FillFlowContainer
+ {
+ Origin = Anchor.BottomLeft,
+ Anchor = Anchor.BottomLeft,
+ Direction = FillDirection.Vertical,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ titleText = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: 18, weight: FontWeight.Regular)
+ },
+ supporterTag = new SupporterIcon
+ {
+ Height = 20,
+ Margin = new MarginPadding { Top = 5 }
+ },
+ new Box
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 1.5f,
+ Margin = new MarginPadding { Top = 10 },
+ Colour = colours.CommunityUserGrayGreenLighter,
+ },
+ new Container
+ {
+ AutoSizeAxes = Axes.Both,
+ Margin = new MarginPadding { Top = 5 },
+ Children = new Drawable[]
+ {
+ userFlag = new DrawableFlag
+ {
+ Size = new Vector2(30, 20)
+ },
+ userCountryText = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: 17.5f, weight: FontWeight.Regular),
+ Margin = new MarginPadding { Left = 40 },
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.CentreLeft,
+ Colour = colours.CommunityUserGrayGreenLighter,
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ }
+ },
+ userStats = new FillFlowContainer
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ AutoSizeAxes = Axes.Y,
+ Width = 300,
+ Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN },
+ Padding = new MarginPadding { Vertical = 15 },
+ Spacing = new Vector2(0, 2)
+ }
+ };
+
+ User.BindValueChanged(user => updateUser(user.NewValue));
+ }
+
+ private void updateUser(User user)
+ {
+ avatar.User = user;
+ usernameText.Text = user?.Username ?? string.Empty;
+ openUserExternally.Link = $@"https://osu.ppy.sh/users/{user?.Id ?? 0}";
+ userFlag.Country = user?.Country;
+ userCountryText.Text = user?.Country?.FullName ?? "Alien";
+ supporterTag.SupportLevel = user?.SupportLevel ?? 0;
+ titleText.Text = user?.Title ?? string.Empty;
+ titleText.Colour = OsuColour.FromHex(user?.Colour ?? "fff");
+
+ userStats.Clear();
+ if (user?.Statistics != null)
+ {
+ userStats.Add(new UserStatsLine("Ranked Score", user.Statistics.RankedScore.ToString("#,##0")));
+ userStats.Add(new UserStatsLine("Hit Accuracy", Math.Round(user.Statistics.Accuracy, 2).ToString("#0.00'%'")));
+ userStats.Add(new UserStatsLine("Play Count", user.Statistics.PlayCount.ToString("#,##0")));
+ userStats.Add(new UserStatsLine("Total Score", user.Statistics.TotalScore.ToString("#,##0")));
+ userStats.Add(new UserStatsLine("Total Hits", user.Statistics.TotalHits.ToString("#,##0")));
+ userStats.Add(new UserStatsLine("Maximum Combo", user.Statistics.MaxCombo.ToString("#,##0")));
+ userStats.Add(new UserStatsLine("Replays Watched by Others", user.Statistics.ReplaysWatched.ToString("#,##0")));
+ }
+ }
+
+ private class UserStatsLine : Container
+ {
+ public UserStatsLine(string left, string right)
+ {
+ RelativeSizeAxes = Axes.X;
+ AutoSizeAxes = Axes.Y;
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: 15),
+ Text = left,
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold),
+ Text = right,
+ },
+ };
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index 138e522cd7..f5233cf70c 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -1,483 +1,152 @@
// 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 osuTK;
-using osuTK.Graphics;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
-using osu.Game.Graphics.Containers;
-using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
-using osu.Game.Overlays.Profile.Components;
using osu.Game.Overlays.Profile.Header;
using osu.Game.Users;
-using Humanizer;
namespace osu.Game.Overlays.Profile
{
public class ProfileHeader : Container
{
- private readonly LinkFlowContainer infoTextLeft;
- private readonly LinkFlowContainer infoTextRight;
- private readonly FillFlowContainer scoreText, scoreNumberText;
- private readonly RankGraph rankGraph;
+ private readonly UserCoverBackground coverContainer;
+ private readonly ProfileHeaderTabControl infoTabControl;
- public readonly SupporterIcon SupporterTag;
- private readonly Container coverContainer;
- private readonly Sprite levelBadge;
- private readonly SpriteText levelText;
- private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA;
- private readonly Box colourBar;
- private readonly DrawableFlag countryFlag;
- private readonly BadgeContainer badgeContainer;
+ private const float cover_height = 150;
+ private const float cover_info_height = 75;
- private const float cover_height = 350;
- private const float info_height = 150;
- private const float info_width = 220;
- private const float avatar_size = 110;
- private const float level_position = 30;
- private const float level_height = 60;
- private const float stats_width = 280;
-
- public ProfileHeader(User user)
+ public ProfileHeader()
{
+ CentreHeaderContainer centreHeaderContainer;
+ DetailHeaderContainer detailHeaderContainer;
+
RelativeSizeAxes = Axes.X;
- Height = cover_height + info_height;
+ AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
- coverContainer = new Container
+ new Container
{
RelativeSizeAxes = Axes.X,
Height = cover_height,
Masking = true,
Children = new Drawable[]
{
+ coverContainer = new UserCoverBackground
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.75f))
+ Colour = ColourInfo.GradientVertical(OsuColour.FromHex("222").Opacity(0.8f), OsuColour.FromHex("222").Opacity(0.2f))
},
- new Container
- {
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN, Bottom = 20, Right = stats_width + UserProfileOverlay.CONTENT_X_MARGIN },
- AutoSizeAxes = Axes.Y,
- RelativeSizeAxes = Axes.X,
- Children = new Drawable[]
- {
- new UpdateableAvatar
- {
- User = user,
- Size = new Vector2(avatar_size),
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- Masking = true,
- CornerRadius = 5,
- OpenOnClick = { Value = false },
- EdgeEffect = new EdgeEffectParameters
- {
- Type = EdgeEffectType.Shadow,
- Colour = Color4.Black.Opacity(0.25f),
- Radius = 4,
- },
- },
- new Container
- {
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- X = avatar_size + 10,
- AutoSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- SupporterTag = new SupporterIcon
- {
- Alpha = 0,
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- Y = -75,
- Size = new Vector2(25, 25)
- },
- new FillFlowContainer
- {
- Direction = FillDirection.Horizontal,
- AutoSizeAxes = Axes.Both,
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- Y = -48,
- Children = new Drawable[]
- {
- usernameText = new OsuSpriteText
- {
- Text = user.Username,
- Font = OsuFont.GetFont(size: 30, weight: FontWeight.Regular, italics: true)
- },
- new ExternalLinkButton($@"https://osu.ppy.sh/users/{user.Id}")
- {
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- Margin = new MarginPadding { Left = 3, Bottom = 3 }, //To better lineup with the font
- },
- }
- },
- countryFlag = new DrawableFlag(user.Country)
- {
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- Width = 30,
- Height = 20
- }
- }
- },
- badgeContainer = new BadgeContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Origin = Anchor.BottomLeft,
- Margin = new MarginPadding { Bottom = 5 },
- Alpha = 0,
- },
- }
- },
- colourBar = new Box
- {
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- X = UserProfileOverlay.CONTENT_X_MARGIN,
- Height = 5,
- Width = info_width,
- Alpha = 0
- }
}
},
- new Box // this is a temporary workaround for incorrect masking behaviour of FillMode.Fill used in UserCoverBackground (see https://github.com/ppy/osu-framework/issues/1675)
- {
- RelativeSizeAxes = Axes.X,
- Height = 1,
- Y = cover_height,
- Colour = OsuColour.Gray(34),
- },
- infoTextLeft = new LinkFlowContainer(t => t.Font = t.Font.With(size: 14))
- {
- X = UserProfileOverlay.CONTENT_X_MARGIN,
- Y = cover_height + 20,
- Width = info_width,
- AutoSizeAxes = Axes.Y,
- ParagraphSpacing = 0.8f,
- LineSpacing = 0.2f
- },
- infoTextRight = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Regular, italics: true))
- {
- X = UserProfileOverlay.CONTENT_X_MARGIN + info_width + 20,
- Y = cover_height + 20,
- Width = info_width,
- AutoSizeAxes = Axes.Y,
- ParagraphSpacing = 0.8f,
- LineSpacing = 0.2f
- },
new Container
{
- X = -UserProfileOverlay.CONTENT_X_MARGIN,
- RelativeSizeAxes = Axes.Y,
- Width = stats_width,
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
+ Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN },
+ Y = cover_height,
+ Height = cover_info_height,
+ RelativeSizeAxes = Axes.X,
+ Anchor = Anchor.TopLeft,
+ Origin = Anchor.BottomLeft,
+ Depth = -float.MaxValue,
Children = new Drawable[]
{
- new Container
+ new ProfileHeaderTitle
{
- RelativeSizeAxes = Axes.X,
- Y = level_position,
- Height = level_height,
- Children = new Drawable[]
- {
- new Box
- {
- Colour = Color4.Black.Opacity(0.5f),
- RelativeSizeAxes = Axes.Both
- },
- levelBadge = new Sprite
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Height = 50,
- Width = 50,
- Alpha = 0
- },
- levelText = new OsuSpriteText
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Y = 11,
- Font = OsuFont.GetFont(size: 20)
- }
- }
+ X = -ScreenTitle.ICON_WIDTH,
},
- new Container
+ infoTabControl = new ProfileHeaderTabControl
{
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
- Y = cover_height,
- Anchor = Anchor.TopCentre,
- Origin = Anchor.BottomCentre,
- Height = cover_height - level_height - level_position - 5,
- Children = new Drawable[]
- {
- new Box
- {
- Colour = Color4.Black.Opacity(0.5f),
- RelativeSizeAxes = Axes.Both
- },
- scoreText = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Vertical,
- Padding = new MarginPadding { Horizontal = 20, Vertical = 18 },
- Spacing = new Vector2(0, 2)
- },
- scoreNumberText = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Vertical,
- Padding = new MarginPadding { Horizontal = 20, Vertical = 18 },
- Spacing = new Vector2(0, 2)
- },
- new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- Y = -64,
- Spacing = new Vector2(20, 0),
- Children = new[]
- {
- gradeSSPlus = new GradeBadge("SSPlus") { Alpha = 0 },
- gradeSS = new GradeBadge("SS") { Alpha = 0 },
- }
- },
- new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- Y = -18,
- Spacing = new Vector2(20, 0),
- Children = new[]
- {
- gradeSPlus = new GradeBadge("SPlus") { Alpha = 0 },
- gradeS = new GradeBadge("S") { Alpha = 0 },
- gradeA = new GradeBadge("A") { Alpha = 0 },
- }
- }
- }
- },
- new Container
- {
- RelativeSizeAxes = Axes.X,
- Anchor = Anchor.BottomCentre,
- Origin = Anchor.BottomCentre,
- Height = info_height - 15,
- Children = new Drawable[]
- {
- new Box
- {
- Colour = Color4.Black.Opacity(0.25f),
- RelativeSizeAxes = Axes.Both
- },
- rankGraph = new RankGraph
- {
- RelativeSizeAxes = Axes.Both
- }
- }
+ Height = cover_info_height - 30,
+ Margin = new MarginPadding { Left = -UserProfileOverlay.CONTENT_X_MARGIN },
+ Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN }
}
}
+ },
+ new FillFlowContainer
+ {
+ Margin = new MarginPadding { Top = cover_height },
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new TopHeaderContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ User = { BindTarget = User },
+ },
+ centreHeaderContainer = new CentreHeaderContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ User = { BindTarget = User },
+ },
+ detailHeaderContainer = new DetailHeaderContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ User = { BindTarget = User },
+ },
+ new MedalHeaderContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ User = { BindTarget = User },
+ },
+ new BottomHeaderContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ User = { BindTarget = User },
+ },
+ }
}
};
+
+ infoTabControl.AddItem("Info");
+ infoTabControl.AddItem("Modding");
+
+ centreHeaderContainer.DetailsVisible.BindValueChanged(visible => detailHeaderContainer.Alpha = visible.NewValue ? 1 : 0, true);
+ User.ValueChanged += e => updateDisplay(e.NewValue);
}
[BackgroundDependencyLoader]
- private void load(TextureStore textures)
+ private void load(OsuColour colours)
{
- levelBadge.Texture = textures.Get(@"Profile/levelbadge");
+ infoTabControl.AccentColour = colours.CommunityUserGreen;
}
- private readonly OsuSpriteText usernameText;
+ public Bindable User = new Bindable();
- private User user;
-
- public User User
+ private void updateDisplay(User user)
{
- get => user;
- set
- {
- user = value;
- loadUser();
- }
+ coverContainer.User = user;
}
- private void loadUser()
+ private class ProfileHeaderTitle : ScreenTitle
{
- LoadComponentAsync(new UserCoverBackground(user)
+ public ProfileHeaderTitle()
{
- RelativeSizeAxes = Axes.Both,
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- FillMode = FillMode.Fill,
- Depth = float.MaxValue,
- }, background =>
- {
- coverContainer.Add(background);
- background.FadeInFromZero(200);
- });
-
- if (user.IsSupporter)
- SupporterTag.Show();
-
- usernameText.Text = user.Username;
-
- if (!string.IsNullOrEmpty(user.Colour))
- {
- colourBar.Colour = OsuColour.FromHex(user.Colour);
- colourBar.Show();
+ Title = "Player";
+ Section = "Info";
}
- void boldItalic(SpriteText t) => t.Font = t.Font.With(Typeface.Exo, weight: FontWeight.Bold, italics: true);
- void lightText(SpriteText t) => t.Alpha = 0.8f;
-
- OsuSpriteText createScoreText(string text) => new OsuSpriteText
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
{
- Font = OsuFont.GetFont(size: 14),
- Text = text
- };
-
- OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText
- {
- Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
- Text = text
- };
-
- if (user.Country != null)
- {
- infoTextLeft.AddText("From ", lightText);
- infoTextLeft.AddText(user.Country.FullName, boldItalic);
- countryFlag.Country = user.Country;
+ AccentColour = colours.CommunityUserGreen;
}
-
- infoTextLeft.NewParagraph();
-
- if (user.JoinDate.ToUniversalTime().Year < 2008)
- {
- infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), lightText);
- }
- else
- {
- infoTextLeft.AddText("Joined ", lightText);
- infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), boldItalic);
- }
-
- if (user.LastVisit.HasValue)
- {
- infoTextLeft.NewLine();
- infoTextLeft.AddText("Last seen ", lightText);
- infoTextLeft.AddText(new DrawableDate(user.LastVisit.Value), boldItalic);
- }
-
- if (user.PlayStyle?.Length > 0)
- {
- infoTextLeft.NewParagraph();
- infoTextLeft.AddText("Plays with ", lightText);
- infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic);
- }
-
- infoTextLeft.NewLine();
- infoTextLeft.AddText("Contributed ", lightText);
- infoTextLeft.AddLink("forum post".ToQuantity(user.PostCount), url: $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: boldItalic);
-
- string websiteWithoutProtcol = user.Website;
- if (!string.IsNullOrEmpty(websiteWithoutProtcol))
- {
- int protocolIndex = websiteWithoutProtcol.IndexOf("//", StringComparison.Ordinal);
- if (protocolIndex >= 0)
- websiteWithoutProtcol = websiteWithoutProtcol.Substring(protocolIndex + 2);
- }
-
- tryAddInfoRightLine(FontAwesome.Solid.MapMarker, user.Location);
- tryAddInfoRightLine(FontAwesome.Regular.Heart, user.Interests);
- tryAddInfoRightLine(FontAwesome.Solid.Suitcase, user.Occupation);
- infoTextRight.NewParagraph();
- if (!string.IsNullOrEmpty(user.Twitter))
- tryAddInfoRightLine(FontAwesome.Brands.Twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
- tryAddInfoRightLine(FontAwesome.Solid.Gamepad, user.Discord);
- tryAddInfoRightLine(FontAwesome.Brands.Skype, user.Skype, @"skype:" + user.Skype + @"?chat");
- tryAddInfoRightLine(FontAwesome.Brands.Lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}");
- tryAddInfoRightLine(FontAwesome.Solid.Globe, websiteWithoutProtcol, user.Website);
-
- if (user.Statistics != null)
- {
- levelBadge.Show();
- levelText.Text = user.Statistics.Level.Current.ToString();
-
- scoreText.Add(createScoreText("Ranked Score"));
- scoreNumberText.Add(createScoreNumberText(user.Statistics.RankedScore.ToString(@"#,0")));
- scoreText.Add(createScoreText("Accuracy"));
- scoreNumberText.Add(createScoreNumberText($"{user.Statistics.Accuracy:0.##}%"));
- scoreText.Add(createScoreText("Play Count"));
- scoreNumberText.Add(createScoreNumberText(user.Statistics.PlayCount.ToString(@"#,0")));
- scoreText.Add(createScoreText("Total Score"));
- scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalScore.ToString(@"#,0")));
- scoreText.Add(createScoreText("Total Hits"));
- scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalHits.ToString(@"#,0")));
- scoreText.Add(createScoreText("Max Combo"));
- scoreNumberText.Add(createScoreNumberText(user.Statistics.MaxCombo.ToString(@"#,0")));
- scoreText.Add(createScoreText("Replays Watched by Others"));
- scoreNumberText.Add(createScoreNumberText(user.Statistics.ReplaysWatched.ToString(@"#,0")));
-
- gradeSSPlus.DisplayCount = user.Statistics.GradesCount.SSPlus;
- gradeSSPlus.Show();
- gradeSS.DisplayCount = user.Statistics.GradesCount.SS;
- gradeSS.Show();
- gradeSPlus.DisplayCount = user.Statistics.GradesCount.SPlus;
- gradeSPlus.Show();
- gradeS.DisplayCount = user.Statistics.GradesCount.S;
- gradeS.Show();
- gradeA.DisplayCount = user.Statistics.GradesCount.A;
- gradeA.Show();
-
- rankGraph.User.Value = user;
- }
-
- badgeContainer.ShowBadges(user.Badges);
- }
-
- private void tryAddInfoRightLine(IconUsage icon, string str, string url = null)
- {
- if (string.IsNullOrEmpty(str)) return;
-
- infoTextRight.AddIcon(icon);
- if (url != null)
- {
- infoTextRight.AddLink(" " + str, url);
- }
- else
- {
- infoTextRight.AddText(" " + str);
- }
-
- infoTextRight.NewLine();
}
}
}
diff --git a/osu.Game/Overlays/Profile/ProfileSection.cs b/osu.Game/Overlays/Profile/ProfileSection.cs
index 6da736432f..4d891384e8 100644
--- a/osu.Game/Overlays/Profile/ProfileSection.cs
+++ b/osu.Game/Overlays/Profile/ProfileSection.cs
@@ -2,13 +2,13 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
-using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
+using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.Profile
diff --git a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs
index a93fefdd75..23fe6e9cd5 100644
--- a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs
+++ b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs
@@ -5,6 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
index 3c69082e9d..aeea5118a7 100644
--- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
+++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs
@@ -7,6 +7,7 @@ using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
diff --git a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs
index a16e852902..93b07fbac7 100644
--- a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs
+++ b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
+using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Configuration;
@@ -9,7 +10,7 @@ namespace osu.Game.Overlays.Settings
{
///
/// A which provides subclasses with the
- /// from the 's .
+ /// from the 's .
///
public abstract class RulesetSettingsSubsection : SettingsSubsection
{
diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
index e4ddc53e17..98eb4b662f 100644
--- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs
@@ -16,6 +16,7 @@ using System.ComponentModel;
using osu.Game.Graphics;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
diff --git a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
index 11cdbf6e0a..9f09f251c2 100644
--- a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
+++ b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
@@ -10,7 +10,7 @@ namespace osu.Game.Overlays.Settings
{
protected override OsuDropdown CreateDropdown() => new DropdownControl();
- protected class DropdownControl : OsuEnumDropdown
+ protected new class DropdownControl : OsuEnumDropdown
{
public DropdownControl()
{
diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs
index 02e9d48f40..e970ff6211 100644
--- a/osu.Game/Overlays/Settings/SettingsItem.cs
+++ b/osu.Game/Overlays/Settings/SettingsItem.cs
@@ -9,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
diff --git a/osu.Game/Overlays/Social/SocialPanel.cs b/osu.Game/Overlays/Social/SocialPanel.cs
index 738f940484..555527670a 100644
--- a/osu.Game/Overlays/Social/SocialPanel.cs
+++ b/osu.Game/Overlays/Social/SocialPanel.cs
@@ -5,7 +5,7 @@ using osuTK;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Input.Events;
using osu.Game.Users;
diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs
index 71374d5180..2b2b19b73a 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs
@@ -4,6 +4,7 @@
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs
index f729810fbc..87b18ba9f4 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs
@@ -1,7 +1,7 @@
// 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.Effects;
using osu.Game.Rulesets;
using osuTK.Graphics;
diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
index d01eab4dab..ebfa6706d4 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
@@ -7,6 +7,7 @@ using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osuTK;
using osuTK.Input;
using osuTK.Graphics;
diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs
index 356ffa5180..c9e49a09f4 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs
@@ -4,7 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Game.Graphics;
using osu.Game.Online.API;
using osu.Game.Users;
diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs
index 48ce055975..3999af8ca4 100644
--- a/osu.Game/Overlays/UserProfileOverlay.cs
+++ b/osu.Game/Overlays/UserProfileOverlay.cs
@@ -5,7 +5,7 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
@@ -31,7 +31,7 @@ namespace osu.Game.Overlays
private SectionsContainer sectionsContainer;
private ProfileTabControl tabs;
- public const float CONTENT_X_MARGIN = 50;
+ public const float CONTENT_X_MARGIN = 70;
public UserProfileOverlay()
{
@@ -81,7 +81,7 @@ namespace osu.Game.Overlays
Show();
- if (user.Id == Header?.User?.Id)
+ if (user.Id == Header?.User.Value?.Id)
return;
userReq?.Cancel();
@@ -113,12 +113,10 @@ namespace osu.Game.Overlays
Colour = OsuColour.Gray(0.2f)
});
- Header = new ProfileHeader(user);
-
Add(sectionsContainer = new SectionsContainer
{
RelativeSizeAxes = Axes.Both,
- ExpandableHeader = Header,
+ ExpandableHeader = Header = new ProfileHeader(),
FixedHeader = tabs,
HeaderBackground = new Box
{
@@ -169,7 +167,7 @@ namespace osu.Game.Overlays
private void userLoadComplete(User user)
{
- Header.User = user;
+ Header.User.Value = user;
if (user.ProfileOrder != null)
{
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index 14f7665e05..5eabe1e936 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -165,7 +165,7 @@ namespace osu.Game.Rulesets.Difficulty
///
/// Creates the s to calculate the difficulty of an .
///
- /// The whose difficulty will be calculated.The whose difficulty will be calculated.
/// The s.
protected abstract Skill[] CreateSkills(IBeatmap beatmap);
}
diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
index 74aa9ace2d..f12591cef4 100644
--- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
+++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs
@@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects;
using osu.Game.Screens.Edit.Compose;
using osuTK;
@@ -108,7 +109,8 @@ namespace osu.Game.Rulesets.Edit
}
///
- /// Invokes , refreshing and parameters for the .
+ /// Invokes ,
+ /// refreshing and parameters for the .
///
protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.Value.Beatmap.ControlPointInfo, beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty);
diff --git a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs
index 85b6c91a07..01992cbbd3 100644
--- a/osu.Game/Rulesets/Edit/SelectionBlueprint.cs
+++ b/osu.Game/Rulesets/Edit/SelectionBlueprint.cs
@@ -68,9 +68,11 @@ namespace osu.Game.Rulesets.Edit
get => state;
set
{
- if (state == value) return;
+ if (state == value)
+ return;
state = value;
+
switch (state)
{
case SelectionState.Selected:
@@ -82,6 +84,8 @@ namespace osu.Game.Rulesets.Edit
Deselected?.Invoke(this);
break;
}
+
+ StateChanged?.Invoke(state);
}
}
diff --git a/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs b/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs
index eb80fa131a..8cefb02904 100644
--- a/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs
+++ b/osu.Game/Rulesets/Mods/IApplicableToBeatmapConverter.cs
@@ -2,14 +2,12 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
-using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mods
{
///
/// Interface for a that applies changes to a .
///
- /// The type of converted .
public interface IApplicableToBeatmapConverter : IApplicableMod
{
///
diff --git a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs
index c13b62812b..f7f81c92c0 100644
--- a/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs
+++ b/osu.Game/Rulesets/Mods/IApplicableToHitObject.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods
public interface IApplicableToHitObject : IApplicableMod
{
///
- /// Applies this to a .
+ /// Applies this to a .
///
/// The to apply to.
void ApplyToHitObject(HitObject hitObject);
diff --git a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs
index 1d0ed94ef4..cb00770868 100644
--- a/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs
+++ b/osu.Game/Rulesets/Mods/IApplicableToScoreProcessor.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Scoring;
+using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mods
{
@@ -10,6 +11,14 @@ namespace osu.Game.Rulesets.Mods
///
public interface IApplicableToScoreProcessor : IApplicableMod
{
+ ///
+ /// Provide a to a mod. Called once on initialisation of a play instance.
+ ///
void ApplyToScoreProcessor(ScoreProcessor scoreProcessor);
+
+ ///
+ /// Called every time a rank calculation is requested. Allows mods to adjust the final rank.
+ ///
+ ScoreRank AdjustRank(ScoreRank rank, double accuracy);
}
}
diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs
index 448ad0eb30..a5e19f293c 100644
--- a/osu.Game/Rulesets/Mods/IMod.cs
+++ b/osu.Game/Rulesets/Mods/IMod.cs
@@ -1,11 +1,12 @@
// 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 Newtonsoft.Json;
namespace osu.Game.Rulesets.Mods
{
- public interface IMod
+ public interface IMod : IEquatable
{
///
/// The shortened name of this mod.
diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs
index d2d0a5bb26..023d37497a 100644
--- a/osu.Game/Rulesets/Mods/Mod.cs
+++ b/osu.Game/Rulesets/Mods/Mod.cs
@@ -70,5 +70,7 @@ namespace osu.Game.Rulesets.Mods
/// Creates a copy of this initialised to a default state.
///
public virtual Mod CreateCopy() => (Mod)Activator.CreateInstance(GetType());
+
+ public bool Equals(IMod other) => GetType() == other?.GetType();
}
}
diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs
index 54331508a0..a477868ad0 100644
--- a/osu.Game/Rulesets/Mods/ModFlashlight.cs
+++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs
@@ -16,6 +16,7 @@ using osu.Game.Graphics;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
+using osu.Game.Scoring;
using osuTK;
using osuTK.Graphics;
@@ -46,6 +47,8 @@ namespace osu.Game.Rulesets.Mods
Combo.BindTo(scoreProcessor.Combo);
}
+ public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
+
public virtual void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset)
{
var flashlight = CreateFlashlight();
@@ -64,25 +67,12 @@ namespace osu.Game.Rulesets.Mods
internal BindableInt Combo;
private IShader shader;
- protected override DrawNode CreateDrawNode() => new FlashlightDrawNode();
+ protected override DrawNode CreateDrawNode() => new FlashlightDrawNode(this);
public override bool RemoveCompletedTransforms => false;
public List Breaks;
- protected override void ApplyDrawNode(DrawNode node)
- {
- base.ApplyDrawNode(node);
-
- var flashNode = (FlashlightDrawNode)node;
-
- flashNode.Shader = shader;
- flashNode.ScreenSpaceDrawQuad = ScreenSpaceDrawQuad;
- flashNode.FlashlightPosition = Vector2Extensions.Transform(FlashlightPosition, DrawInfo.Matrix);
- flashNode.FlashlightSize = FlashlightSize * DrawInfo.Matrix.ExtractScale().Xy;
- flashNode.FlashlightDim = FlashlightDim;
- }
-
[BackgroundDependencyLoader]
private void load(ShaderManager shaderManager)
{
@@ -152,29 +142,47 @@ namespace osu.Game.Rulesets.Mods
Invalidate(Invalidation.DrawNode);
}
}
- }
- private class FlashlightDrawNode : DrawNode
- {
- public IShader Shader;
- public Quad ScreenSpaceDrawQuad;
- public Vector2 FlashlightPosition;
- public Vector2 FlashlightSize;
- public float FlashlightDim;
-
- public override void Draw(Action vertexAction)
+ private class FlashlightDrawNode : DrawNode
{
- base.Draw(vertexAction);
+ protected new Flashlight Source => (Flashlight)base.Source;
- Shader.Bind();
+ private IShader shader;
+ private Quad screenSpaceDrawQuad;
+ private Vector2 flashlightPosition;
+ private Vector2 flashlightSize;
+ private float flashlightDim;
- Shader.GetUniform("flashlightPos").UpdateValue(ref FlashlightPosition);
- Shader.GetUniform("flashlightSize").UpdateValue(ref FlashlightSize);
- Shader.GetUniform("flashlightDim").UpdateValue(ref FlashlightDim);
+ public FlashlightDrawNode(Flashlight source)
+ : base(source)
+ {
+ }
- Texture.WhitePixel.DrawQuad(ScreenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction);
+ public override void ApplyState()
+ {
+ base.ApplyState();
- Shader.Unbind();
+ shader = Source.shader;
+ screenSpaceDrawQuad = Source.ScreenSpaceDrawQuad;
+ flashlightPosition = Vector2Extensions.Transform(Source.FlashlightPosition, DrawInfo.Matrix);
+ flashlightSize = Source.FlashlightSize * DrawInfo.Matrix.ExtractScale().Xy;
+ flashlightDim = Source.FlashlightDim;
+ }
+
+ public override void Draw(Action vertexAction)
+ {
+ base.Draw(vertexAction);
+
+ shader.Bind();
+
+ shader.GetUniform("flashlightPos").UpdateValue(ref flashlightPosition);
+ shader.GetUniform("flashlightSize").UpdateValue(ref flashlightSize);
+ shader.GetUniform("flashlightDim").UpdateValue(ref FlashlightDim);
+
+ Texture.WhitePixel.DrawQuad(screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction);
+
+ shader.Unbind();
+ }
}
}
}
diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs
index c7e3f0a78f..ea1c56623f 100644
--- a/osu.Game/Rulesets/Mods/ModHidden.cs
+++ b/osu.Game/Rulesets/Mods/ModHidden.cs
@@ -8,10 +8,12 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModHidden : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
+ public abstract class ModHidden : Mod, IReadFromConfig, IApplicableToDrawableHitObjects, IApplicableToScoreProcessor
{
public override string Name => "Hidden";
public override string Acronym => "HD";
@@ -32,6 +34,25 @@ namespace osu.Game.Rulesets.Mods
d.ApplyCustomUpdateState += ApplyHiddenState;
}
+ public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
+ {
+ // Default value of ScoreProcessor's Rank in Hidden Mod should be SS+
+ scoreProcessor.Rank.Value = ScoreRank.XH;
+ }
+
+ public ScoreRank AdjustRank(ScoreRank rank, double accuracy)
+ {
+ switch (rank)
+ {
+ case ScoreRank.X:
+ return ScoreRank.XH;
+ case ScoreRank.S:
+ return ScoreRank.SH;
+ default:
+ return rank;
+ }
+ }
+
protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state)
{
}
diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
index 6a82050d26..809661db8e 100644
--- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
+++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs
@@ -5,6 +5,7 @@ using System;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mods
{
@@ -24,6 +25,8 @@ namespace osu.Game.Rulesets.Mods
scoreProcessor.FailConditions += FailCondition;
}
+ public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
+
protected virtual bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Combo.Value == 0;
}
}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index a7cfbd3300..2e983b8fe1 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -7,6 +7,7 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.TypeExtensions;
+using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Audio;
using osu.Game.Graphics;
@@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
public bool AllJudged => Judged && NestedHitObjects.All(h => h.AllJudged);
///
- /// Whether this has been hit. This occurs if is .
+ /// Whether this has been hit. This occurs if is hit.
/// Note: This does NOT include nested hitobjects.
///
public bool IsHit => Result?.IsHit ?? false;
@@ -223,7 +224,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
}
///
- /// Will called at least once after the of this has been passed.
+ /// Will called at least once after the of this has been passed.
///
internal void OnLifetimeEnd()
{
diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs
index fd542be67d..cede2e50d0 100644
--- a/osu.Game/Rulesets/Objects/HitObject.cs
+++ b/osu.Game/Rulesets/Objects/HitObject.cs
@@ -53,8 +53,6 @@ namespace osu.Game.Rulesets.Objects
[JsonIgnore]
public bool Kiai { get; private set; }
- private float overallDifficulty = BeatmapDifficulty.DEFAULT_DIFFICULTY;
-
///
/// The hit windows for this .
///
@@ -115,7 +113,7 @@ namespace osu.Game.Rulesets.Objects
/// Creates the for this .
/// This can be null to indicate that the has no .
///
- /// This will only be invoked if hasn't been set externally (e.g. from a .
+ /// This will only be invoked if hasn't been set externally (e.g. from a .
///
///
protected virtual HitWindows CreateHitWindows() => new HitWindows();
diff --git a/osu.Game/Rulesets/Objects/HitWindows.cs b/osu.Game/Rulesets/Objects/HitWindows.cs
index c5b7686da6..589c72957b 100644
--- a/osu.Game/Rulesets/Objects/HitWindows.cs
+++ b/osu.Game/Rulesets/Objects/HitWindows.cs
@@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Objects
///
/// Given a time offset, whether the can ever be hit in the future with a non- result.
- /// This happens if is less than what is required for a result.
+ /// This happens if is less than what is required for .
///
/// The time offset.
/// Whether the can be hit at any point in the future from this time offset.
diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs
index 1e9767a54f..e312b004ba 100644
--- a/osu.Game/Rulesets/Objects/SliderPath.cs
+++ b/osu.Game/Rulesets/Objects/SliderPath.cs
@@ -277,12 +277,5 @@ namespace osu.Game.Rulesets.Objects
return ControlPoints.SequenceEqual(other.ControlPoints) && ExpectedDistance.Equals(other.ExpectedDistance) && Type == other.Type;
}
-
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj)) return false;
-
- return obj is SliderPath other && Equals(other);
- }
}
}
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index 3521c17b23..42b1322cae 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -53,6 +53,7 @@ namespace osu.Game.Rulesets
/// Attempt to create a hit renderer for a beatmap
///
/// The beatmap to create the hit renderer for.
+ /// The s to apply.
/// Unable to successfully load the beatmap to be usable with this ruleset.
///
public abstract DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList mods);
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index ba71e1e9b2..cc52b0a038 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -10,6 +10,7 @@ using osu.Framework.Extensions;
using osu.Framework.Extensions.TypeExtensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
@@ -60,6 +61,11 @@ namespace osu.Game.Rulesets.Scoring
///
public readonly BindableInt Combo = new BindableInt();
+ ///
+ /// The current selected mods
+ ///
+ public readonly Bindable> Mods = new Bindable>(Array.Empty());
+
///
/// Create a for this processor.
///
@@ -98,7 +104,12 @@ namespace osu.Game.Rulesets.Scoring
protected ScoreProcessor()
{
Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); };
- Accuracy.ValueChanged += delegate { Rank.Value = rankFrom(Accuracy.Value); };
+ Accuracy.ValueChanged += delegate
+ {
+ Rank.Value = rankFrom(Accuracy.Value);
+ foreach (var mod in Mods.Value.OfType())
+ Rank.Value = mod.AdjustRank(Rank.Value, Accuracy.Value);
+ };
}
private ScoreRank rankFrom(double acc)
@@ -154,7 +165,6 @@ namespace osu.Game.Rulesets.Scoring
///
/// Notifies subscribers of that a new judgement has occurred.
///
- /// The judgement to notify subscribers of.
/// The judgement scoring result to notify subscribers of.
protected void NotifyNewJudgement(JudgementResult result)
{
@@ -283,7 +293,6 @@ namespace osu.Game.Rulesets.Scoring
///
/// Reverts the score change of a that was applied to this .
///
- /// The judgement to remove.
/// The judgement scoring result.
private void revertResult(JudgementResult result)
{
@@ -340,7 +349,6 @@ namespace osu.Game.Rulesets.Scoring
///
/// Reverts the score change of a that was applied to this .
///
- /// The judgement to remove.
/// The judgement scoring result.
protected virtual void RevertResult(JudgementResult result)
{
diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs
index 01ae637158..df9effb321 100644
--- a/osu.Game/Rulesets/UI/DrawableRuleset.cs
+++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs
@@ -93,6 +93,7 @@ namespace osu.Game.Rulesets.UI
///
/// The ruleset being represented.
/// The beatmap to create the hit renderer for.
+ /// The s to apply.
protected DrawableRuleset(Ruleset ruleset, WorkingBeatmap workingBeatmap, IReadOnlyList mods)
: base(ruleset)
{
@@ -275,7 +276,8 @@ namespace osu.Game.Rulesets.UI
///
/// Applies the active mods to this DrawableRuleset.
///
- ///
+ /// The s to apply.
+ /// The to apply.
private void applyRulesetMods(IReadOnlyList mods, OsuConfigManager config)
{
if (mods == null)
diff --git a/osu.Game/Rulesets/UI/GameplayCursorContainer.cs b/osu.Game/Rulesets/UI/GameplayCursorContainer.cs
index de73c08809..41edfa0b68 100644
--- a/osu.Game/Rulesets/UI/GameplayCursorContainer.cs
+++ b/osu.Game/Rulesets/UI/GameplayCursorContainer.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.UI
public class GameplayCursorContainer : CursorContainer
{
///
- /// Because Show/Hide are executed by a parent, is updated immediately even if the cursor
+ /// Because Show/Hide are executed by a parent, is updated immediately even if the cursor
/// is in a non-updating state (via limitations).
///
/// This holds the true visibility value.
diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs
index a99c16a610..a073ad246b 100644
--- a/osu.Game/Rulesets/UI/Playfield.cs
+++ b/osu.Game/Rulesets/UI/Playfield.cs
@@ -100,7 +100,6 @@ namespace osu.Game.Rulesets.UI
///
/// Provide an optional cursor which is to be used for gameplay.
- /// If providing a cursor, must also point to a valid target container.
///
/// The cursor, or null if a cursor is not rqeuired.
protected virtual GameplayCursorContainer CreateCursor() => null;
diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs
index a104b0629f..b7a5eedc22 100644
--- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs
@@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms
/// The current time.
/// The amount of visible time.
/// The absolute spatial length through .
- /// The time at which == .
+ /// The time at which == .
double TimeAt(float position, double currentTime, double timeRange, float scrollLength);
///
diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
index dbe8d8c299..f21d0b4a66 100644
--- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
protected virtual ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Sequential;
///
- /// Whether the player can change .
+ /// Whether the player can change .
///
protected virtual bool UserScrollSpeedAdjustment => true;
diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs
index d36f963016..8bdc30ac94 100644
--- a/osu.Game/Scoring/ScoreInfo.cs
+++ b/osu.Game/Scoring/ScoreInfo.cs
@@ -177,6 +177,8 @@ namespace osu.Game.Scoring
protected class DeserializedMod : IMod
{
public string Acronym { get; set; }
+
+ public bool Equals(IMod other) => Acronym == other?.Acronym;
}
public override string ToString() => $"{User} playing {Beatmap}";
diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs
index 5f82329496..9c0c5da0fb 100644
--- a/osu.Game/Screens/BackgroundScreenStack.cs
+++ b/osu.Game/Screens/BackgroundScreenStack.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Screens
//public float ParallaxAmount { set => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * value; }
- public new void Push(BackgroundScreen screen)
+ public void Push(BackgroundScreen screen)
{
if (screen == null)
return;
diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs
index 1ad69afe91..70c0cf623e 100644
--- a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs
+++ b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs
@@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
index bcb2bee601..11e649168f 100644
--- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs
@@ -121,6 +121,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// Handle a blueprint requesting selection.
///
/// The blueprint.
+ /// The input state at the point of selection.
internal void HandleSelectionRequested(SelectionBlueprint blueprint, InputState state)
{
if (state.Keyboard.ControlPressed)
@@ -166,8 +167,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
var topLeft = new Vector2(float.MaxValue, float.MaxValue);
var bottomRight = new Vector2(float.MinValue, float.MinValue);
- bool hasSelection = false;
-
foreach (var blueprint in selectedBlueprints)
{
topLeft = Vector2.ComponentMin(topLeft, ToLocalSpace(blueprint.SelectionQuad.TopLeft));
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
index f41b3cddc0..9b00a3998d 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/ZoomableScrollContainer.cs
@@ -131,7 +131,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private readonly float scrollOffset;
///
- /// Transforms to a new value.
+ /// Transforms to a new value.
///
/// The focus point in absolute coordinates local to the content.
/// The size of the content.
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 0ba1e74aca..09977454f0 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -65,9 +65,6 @@ namespace osu.Game.Screens.Edit
dependencies.Cache(beatDivisor);
EditorMenuBar menuBar;
- TimeInfoContainer timeInfo;
- SummaryTimeline timeline;
- PlaybackControl playback;
var fileMenuItems = new List