mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 00:42:55 +08:00
Merge pull request #9086 from smoogipoo/results-screen-condensed-panel
Implement contracted score panel and integrate into results screen
This commit is contained in:
commit
e64e44ecf9
@ -8,7 +8,7 @@ using osu.Game.Rulesets.Scoring;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Scoring
|
namespace osu.Game.Rulesets.Osu.Scoring
|
||||||
{
|
{
|
||||||
internal class OsuScoreProcessor : ScoreProcessor
|
public class OsuScoreProcessor : ScoreProcessor
|
||||||
{
|
{
|
||||||
protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement);
|
protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement);
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable));
|
AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable));
|
||||||
AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded));
|
AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded));
|
||||||
createButton(false);
|
createButton(false);
|
||||||
|
createButtonNoScore();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createButton(bool withReplay)
|
private void createButton(bool withReplay)
|
||||||
@ -40,6 +41,22 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for load", () => downloadButton.IsLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createButtonNoScore()
|
||||||
|
{
|
||||||
|
AddStep("create button with null score", () =>
|
||||||
|
{
|
||||||
|
Child = downloadButton = new TestReplayDownloadButton(null)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for load", () => downloadButton.IsLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScoreInfo getScoreInfo(bool replayAvailable)
|
private ScoreInfo getScoreInfo(bool replayAvailable)
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Ranking;
|
||||||
|
using osu.Game.Screens.Ranking.Contracted;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Ranking
|
||||||
|
{
|
||||||
|
public class TestSceneContractedPanelMiddleContent : OsuTestScene
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private RulesetStore rulesetStore { get; set; }
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShowPanel()
|
||||||
|
{
|
||||||
|
AddStep("show example score", () => showPanel(CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)), new TestScoreInfo(new OsuRuleset().RulesetInfo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score)
|
||||||
|
{
|
||||||
|
Child = new ContractedPanelMiddleContentContainer(workingBeatmap, score);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ContractedPanelMiddleContentContainer : Container
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private Bindable<WorkingBeatmap> workingBeatmap { get; set; }
|
||||||
|
|
||||||
|
public ContractedPanelMiddleContentContainer(WorkingBeatmap beatmap, ScoreInfo score)
|
||||||
|
{
|
||||||
|
workingBeatmap = new Bindable<WorkingBeatmap>(beatmap);
|
||||||
|
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
Size = new Vector2(ScorePanel.CONTRACTED_WIDTH, 460);
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4Extensions.FromHex("#353535"),
|
||||||
|
},
|
||||||
|
new ContractedPanelMiddleContent(score),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -14,10 +13,7 @@ using osu.Framework.Testing;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Screens.Ranking.Expanded;
|
using osu.Game.Screens.Ranking.Expanded;
|
||||||
@ -37,7 +33,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
{
|
{
|
||||||
var author = new User { Username = "mapper_name" };
|
var author = new User { Username = "mapper_name" };
|
||||||
|
|
||||||
AddStep("show example score", () => showPanel(createTestBeatmap(author), createTestScore()));
|
AddStep("show example score", () => showPanel(createTestBeatmap(author), new TestScoreInfo(new OsuRuleset().RulesetInfo)));
|
||||||
|
|
||||||
AddAssert("mapper name present", () => this.ChildrenOfType<OsuSpriteText>().Any(spriteText => spriteText.Text == "mapper_name"));
|
AddAssert("mapper name present", () => this.ChildrenOfType<OsuSpriteText>().Any(spriteText => spriteText.Text == "mapper_name"));
|
||||||
}
|
}
|
||||||
@ -45,7 +41,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestMapWithUnknownMapper()
|
public void TestMapWithUnknownMapper()
|
||||||
{
|
{
|
||||||
AddStep("show example score", () => showPanel(createTestBeatmap(null), createTestScore()));
|
AddStep("show example score", () => showPanel(createTestBeatmap(null), new TestScoreInfo(new OsuRuleset().RulesetInfo)));
|
||||||
|
|
||||||
AddAssert("mapped by text not present", () =>
|
AddAssert("mapped by text not present", () =>
|
||||||
this.ChildrenOfType<OsuSpriteText>().All(spriteText => !containsAny(spriteText.Text, "mapped", "by")));
|
this.ChildrenOfType<OsuSpriteText>().All(spriteText => !containsAny(spriteText.Text, "mapped", "by")));
|
||||||
@ -66,29 +62,6 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
return new TestWorkingBeatmap(beatmap);
|
return new TestWorkingBeatmap(beatmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScoreInfo createTestScore() => new ScoreInfo
|
|
||||||
{
|
|
||||||
User = new User
|
|
||||||
{
|
|
||||||
Id = 2,
|
|
||||||
Username = "peppy",
|
|
||||||
},
|
|
||||||
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
|
|
||||||
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
|
|
||||||
TotalScore = 999999,
|
|
||||||
Accuracy = 0.95,
|
|
||||||
MaxCombo = 999,
|
|
||||||
Rank = ScoreRank.S,
|
|
||||||
Date = DateTimeOffset.Now,
|
|
||||||
Statistics =
|
|
||||||
{
|
|
||||||
{ HitResult.Miss, 1 },
|
|
||||||
{ HitResult.Meh, 50 },
|
|
||||||
{ HitResult.Good, 100 },
|
|
||||||
{ HitResult.Great, 300 },
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains);
|
private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains);
|
||||||
|
|
||||||
private class ExpandedPanelMiddleContentContainer : Container
|
private class ExpandedPanelMiddleContentContainer : Container
|
||||||
|
@ -5,8 +5,8 @@ using osu.Framework.Extensions.Color4Extensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Screens.Ranking.Expanded;
|
using osu.Game.Screens.Ranking.Expanded;
|
||||||
using osu.Game.Users;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Ranking
|
namespace osu.Game.Tests.Visual.Ranking
|
||||||
@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4Extensions.FromHex("#444"),
|
Colour = Color4Extensions.FromHex("#444"),
|
||||||
},
|
},
|
||||||
new ExpandedPanelTopContent(new User { Id = 2, Username = "peppy" }),
|
new ExpandedPanelTopContent(new TestScoreInfo(new OsuRuleset().RulesetInfo).User),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -11,13 +9,10 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Tests.Beatmaps;
|
|
||||||
using osu.Game.Users;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Ranking
|
namespace osu.Game.Tests.Visual.Ranking
|
||||||
{
|
{
|
||||||
@ -41,26 +36,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
|
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestSoloResults createResultsScreen() => new TestSoloResults(new ScoreInfo
|
private TestSoloResults createResultsScreen() => new TestSoloResults(new TestScoreInfo(new OsuRuleset().RulesetInfo));
|
||||||
{
|
|
||||||
TotalScore = 2845370,
|
|
||||||
Accuracy = 0.98,
|
|
||||||
MaxCombo = 123,
|
|
||||||
Rank = ScoreRank.A,
|
|
||||||
Date = DateTimeOffset.Now,
|
|
||||||
Statistics = new Dictionary<HitResult, int>
|
|
||||||
{
|
|
||||||
{ HitResult.Great, 50 },
|
|
||||||
{ HitResult.Good, 20 },
|
|
||||||
{ HitResult.Meh, 50 },
|
|
||||||
{ HitResult.Miss, 1 }
|
|
||||||
},
|
|
||||||
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
|
|
||||||
User = new User
|
|
||||||
{
|
|
||||||
Username = "peppy",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void ResultsWithoutPlayer()
|
public void ResultsWithoutPlayer()
|
||||||
|
@ -1,28 +1,23 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Tests.Beatmaps;
|
|
||||||
using osu.Game.Users;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Ranking
|
namespace osu.Game.Tests.Visual.Ranking
|
||||||
{
|
{
|
||||||
public class TestSceneScorePanel : OsuTestScene
|
public class TestSceneScorePanel : OsuTestScene
|
||||||
{
|
{
|
||||||
|
private ScorePanel panel;
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDRank()
|
public void TestDRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.5, Rank = ScoreRank.D };
|
||||||
score.Accuracy = 0.5;
|
|
||||||
score.Rank = ScoreRank.D;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -30,9 +25,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestCRank()
|
public void TestCRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.75, Rank = ScoreRank.C };
|
||||||
score.Accuracy = 0.75;
|
|
||||||
score.Rank = ScoreRank.C;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -40,9 +33,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestBRank()
|
public void TestBRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.85, Rank = ScoreRank.B };
|
||||||
score.Accuracy = 0.85;
|
|
||||||
score.Rank = ScoreRank.B;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -50,9 +41,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestARank()
|
public void TestARank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.925, Rank = ScoreRank.A };
|
||||||
score.Accuracy = 0.925;
|
|
||||||
score.Rank = ScoreRank.A;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -60,9 +49,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSRank()
|
public void TestSRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.975, Rank = ScoreRank.S };
|
||||||
score.Accuracy = 0.975;
|
|
||||||
score.Rank = ScoreRank.S;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -70,9 +57,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestAlmostSSRank()
|
public void TestAlmostSSRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.9999, Rank = ScoreRank.S };
|
||||||
score.Accuracy = 0.9999;
|
|
||||||
score.Rank = ScoreRank.S;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -80,9 +65,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSSRank()
|
public void TestSSRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 1, Rank = ScoreRank.X };
|
||||||
score.Accuracy = 1;
|
|
||||||
score.Rank = ScoreRank.X;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -90,44 +73,42 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestAllHitResults()
|
public void TestAllHitResults()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Statistics = { [HitResult.Perfect] = 350, [HitResult.Ok] = 200 } };
|
||||||
score.Statistics[HitResult.Perfect] = 350;
|
|
||||||
score.Statistics[HitResult.Ok] = 200;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPanelStep(ScoreInfo score) => AddStep("add panel", () =>
|
[Test]
|
||||||
|
public void TestContractedPanel()
|
||||||
{
|
{
|
||||||
Child = new ScorePanel(score)
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.925, Rank = ScoreRank.A };
|
||||||
|
|
||||||
|
addPanelStep(score, PanelState.Contracted);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestExpandAndContract()
|
||||||
|
{
|
||||||
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.925, Rank = ScoreRank.A };
|
||||||
|
|
||||||
|
addPanelStep(score, PanelState.Contracted);
|
||||||
|
AddWaitStep("wait for transition", 10);
|
||||||
|
|
||||||
|
AddStep("expand panel", () => panel.State = PanelState.Expanded);
|
||||||
|
AddWaitStep("wait for transition", 10);
|
||||||
|
|
||||||
|
AddStep("contract panel", () => panel.State = PanelState.Contracted);
|
||||||
|
AddWaitStep("wait for transition", 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPanelStep(ScoreInfo score, PanelState state = PanelState.Expanded) => AddStep("add panel", () =>
|
||||||
|
{
|
||||||
|
Child = panel = new ScorePanel(score)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
State = PanelState.Expanded
|
State = state
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
private ScoreInfo createScore() => new ScoreInfo
|
|
||||||
{
|
|
||||||
User = new User
|
|
||||||
{
|
|
||||||
Id = 2,
|
|
||||||
Username = "peppy",
|
|
||||||
},
|
|
||||||
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
|
|
||||||
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
|
|
||||||
TotalScore = 2845370,
|
|
||||||
Accuracy = 0.95,
|
|
||||||
MaxCombo = 999,
|
|
||||||
Rank = ScoreRank.S,
|
|
||||||
Date = DateTimeOffset.Now,
|
|
||||||
Statistics =
|
|
||||||
{
|
|
||||||
{ HitResult.Miss, 1 },
|
|
||||||
{ HitResult.Meh, 50 },
|
|
||||||
{ HitResult.Good, 100 },
|
|
||||||
{ HitResult.Great, 300 },
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
208
osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs
Normal file
208
osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Ranking;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Ranking
|
||||||
|
{
|
||||||
|
public class TestSceneScorePanelList : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private ScorePanelList list;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEmptyList()
|
||||||
|
{
|
||||||
|
createListStep(() => new ScorePanelList());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEmptyListWithSelectedScore()
|
||||||
|
{
|
||||||
|
createListStep(() => new ScorePanelList
|
||||||
|
{
|
||||||
|
SelectedScore = { Value = new TestScoreInfo(new OsuRuleset().RulesetInfo) }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddPanelAfterSelectingScore()
|
||||||
|
{
|
||||||
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
|
createListStep(() => new ScorePanelList
|
||||||
|
{
|
||||||
|
SelectedScore = { Value = score }
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("add panel", () => list.AddScore(score));
|
||||||
|
|
||||||
|
assertScoreState(score, true);
|
||||||
|
assertExpandedPanelCentred();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddPanelBeforeSelectingScore()
|
||||||
|
{
|
||||||
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
|
createListStep(() => new ScorePanelList());
|
||||||
|
|
||||||
|
AddStep("add panel", () => list.AddScore(score));
|
||||||
|
|
||||||
|
assertScoreState(score, false);
|
||||||
|
assertFirstPanelCentred();
|
||||||
|
|
||||||
|
AddStep("select score", () => list.SelectedScore.Value = score);
|
||||||
|
|
||||||
|
assertScoreState(score, true);
|
||||||
|
assertExpandedPanelCentred();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddManyNonExpandedPanels()
|
||||||
|
{
|
||||||
|
createListStep(() => new ScorePanelList());
|
||||||
|
|
||||||
|
AddStep("add many scores", () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 20; i++)
|
||||||
|
list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo));
|
||||||
|
});
|
||||||
|
|
||||||
|
assertFirstPanelCentred();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddManyScoresAfterExpandedPanel()
|
||||||
|
{
|
||||||
|
var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
|
createListStep(() => new ScorePanelList());
|
||||||
|
|
||||||
|
AddStep("add initial panel and select", () =>
|
||||||
|
{
|
||||||
|
list.AddScore(initialScore);
|
||||||
|
list.SelectedScore.Value = initialScore;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("add many scores", () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 20; i++)
|
||||||
|
list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore - i - 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
assertScoreState(initialScore, true);
|
||||||
|
assertExpandedPanelCentred();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddManyScoresBeforeExpandedPanel()
|
||||||
|
{
|
||||||
|
var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
|
createListStep(() => new ScorePanelList());
|
||||||
|
|
||||||
|
AddStep("add initial panel and select", () =>
|
||||||
|
{
|
||||||
|
list.AddScore(initialScore);
|
||||||
|
list.SelectedScore.Value = initialScore;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("add scores", () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 20; i++)
|
||||||
|
list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore + i + 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
assertScoreState(initialScore, true);
|
||||||
|
assertExpandedPanelCentred();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddManyPanelsOnBothSidesOfExpandedPanel()
|
||||||
|
{
|
||||||
|
var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
|
createListStep(() => new ScorePanelList());
|
||||||
|
|
||||||
|
AddStep("add initial panel and select", () =>
|
||||||
|
{
|
||||||
|
list.AddScore(initialScore);
|
||||||
|
list.SelectedScore.Value = initialScore;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("add scores after", () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 20; i++)
|
||||||
|
list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore - i - 1 });
|
||||||
|
|
||||||
|
for (int i = 0; i < 20; i++)
|
||||||
|
list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore + i + 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
assertScoreState(initialScore, true);
|
||||||
|
assertExpandedPanelCentred();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSelectMultipleScores()
|
||||||
|
{
|
||||||
|
var firstScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
|
||||||
|
var secondScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
|
createListStep(() => new ScorePanelList());
|
||||||
|
|
||||||
|
AddStep("add scores and select first", () =>
|
||||||
|
{
|
||||||
|
list.AddScore(firstScore);
|
||||||
|
list.AddScore(secondScore);
|
||||||
|
list.SelectedScore.Value = firstScore;
|
||||||
|
});
|
||||||
|
|
||||||
|
assertScoreState(firstScore, true);
|
||||||
|
assertScoreState(secondScore, false);
|
||||||
|
|
||||||
|
AddStep("select second score", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(list.ChildrenOfType<ScorePanel>().Single(p => p.Score == secondScore));
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertScoreState(firstScore, false);
|
||||||
|
assertScoreState(secondScore, true);
|
||||||
|
assertExpandedPanelCentred();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createListStep(Func<ScorePanelList> creationFunc)
|
||||||
|
{
|
||||||
|
AddStep("create list", () => Child = list = creationFunc().With(d =>
|
||||||
|
{
|
||||||
|
d.Anchor = Anchor.Centre;
|
||||||
|
d.Origin = Anchor.Centre;
|
||||||
|
}));
|
||||||
|
|
||||||
|
AddUntilStep("wait for load", () => list.IsLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertExpandedPanelCentred() => AddUntilStep("expanded panel centred", () =>
|
||||||
|
{
|
||||||
|
var expandedPanel = list.ChildrenOfType<ScorePanel>().Single(p => p.State == PanelState.Expanded);
|
||||||
|
return Precision.AlmostEquals(expandedPanel.ScreenSpaceDrawQuad.Centre.X, list.ScreenSpaceDrawQuad.Centre.X, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
private void assertFirstPanelCentred()
|
||||||
|
=> AddUntilStep("first panel centred", () => Precision.AlmostEquals(list.ChildrenOfType<ScorePanel>().First().ScreenSpaceDrawQuad.Centre.X, list.ScreenSpaceDrawQuad.Centre.X, 1));
|
||||||
|
|
||||||
|
private void assertScoreState(ScoreInfo score, bool expanded)
|
||||||
|
=> AddUntilStep($"score expanded = {expanded}", () => (list.ChildrenOfType<ScorePanel>().Single(p => p.Score == score).State == PanelState.Expanded) == expanded);
|
||||||
|
}
|
||||||
|
}
|
@ -479,7 +479,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value;
|
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value;
|
||||||
|
|
||||||
protected virtual ResultsScreen CreateResults(ScoreInfo score) => new ResultsScreen(score);
|
protected virtual ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score);
|
||||||
|
|
||||||
#region Fail Logic
|
#region Fail Logic
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Screens.Play
|
|||||||
this.Push(CreateResults(DrawableRuleset.ReplayScore.ScoreInfo));
|
this.Push(CreateResults(DrawableRuleset.ReplayScore.ScoreInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ResultsScreen CreateResults(ScoreInfo score) => new ResultsScreen(score, false);
|
protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false);
|
||||||
|
|
||||||
protected override ScoreInfo CreateScore() => score.ScoreInfo;
|
protected override ScoreInfo CreateScore() => score.ScoreInfo;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,222 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
|
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.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online.Leaderboards;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Users.Drawables;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Ranking.Contracted
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The content that appears in the middle of a contracted <see cref="ScorePanel"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class ContractedPanelMiddleContent : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly ScoreInfo score;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ContractedPanelMiddleContent"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="score">The <see cref="ScoreInfo"/> to display.</param>
|
||||||
|
public ContractedPanelMiddleContent(ScoreInfo score)
|
||||||
|
{
|
||||||
|
this.score = score;
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
InternalChild = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
CornerExponent = 2.5f,
|
||||||
|
CornerRadius = 20,
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Colour = Color4.Black.Opacity(0.25f),
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Radius = 1,
|
||||||
|
Offset = new Vector2(0, 4)
|
||||||
|
},
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4Extensions.FromHex("444")
|
||||||
|
},
|
||||||
|
new UserCoverBackground
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
User = score.User,
|
||||||
|
Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.5f), Color4Extensions.FromHex("#444").Opacity(0))
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding(10),
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, 10),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new UpdateableAvatar(score.User)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Size = new Vector2(110),
|
||||||
|
Masking = true,
|
||||||
|
CornerExponent = 2.5f,
|
||||||
|
CornerRadius = 20,
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Colour = Color4.Black.Opacity(0.25f),
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Radius = 8,
|
||||||
|
Offset = new Vector2(0, 4),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Text = score.UserString,
|
||||||
|
Font = OsuFont.GetFont(size: 16, weight: FontWeight.SemiBold)
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, 5),
|
||||||
|
ChildrenEnumerable = score.SortedStatistics.Select(s => createStatistic(s.Key.GetDescription(), s.Value.ToString()))
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Margin = new MarginPadding { Top = 10 },
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, 5),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
createStatistic("Max Combo", $"x{score.MaxCombo}"),
|
||||||
|
createStatistic("Accuracy", $"{score.Accuracy.FormatAccuracy()}"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new ModDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
ExpansionMode = ExpansionMode.AlwaysExpanded,
|
||||||
|
DisplayUnrankedText = false,
|
||||||
|
Current = { Value = score.Mods },
|
||||||
|
Scale = new Vector2(0.5f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Vertical = 5 },
|
||||||
|
Child = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Text = score.TotalScore.ToString("N0"),
|
||||||
|
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, fixedWidth: true),
|
||||||
|
Spacing = new Vector2(-1, 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Top = 2 },
|
||||||
|
Child = new DrawableRank(score.Rank)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RowDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RowDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.Absolute, 45),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable createStatistic(string key, string value) => new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Text = key,
|
||||||
|
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold)
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
Text = value,
|
||||||
|
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
|
||||||
|
Colour = Color4Extensions.FromHex("#FFDD55")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -13,6 +14,8 @@ namespace osu.Game.Screens.Ranking
|
|||||||
{
|
{
|
||||||
public class ReplayDownloadButton : DownloadTrackingComposite<ScoreInfo, ScoreManager>
|
public class ReplayDownloadButton : DownloadTrackingComposite<ScoreInfo, ScoreManager>
|
||||||
{
|
{
|
||||||
|
public Bindable<ScoreInfo> Score => Model;
|
||||||
|
|
||||||
private DownloadButton button;
|
private DownloadButton button;
|
||||||
private ShakeContainer shakeContainer;
|
private ShakeContainer shakeContainer;
|
||||||
|
|
||||||
@ -23,7 +26,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
if (State.Value == DownloadState.LocallyAvailable)
|
if (State.Value == DownloadState.LocallyAvailable)
|
||||||
return ReplayAvailability.Local;
|
return ReplayAvailability.Local;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Model.Value.Hash))
|
if (!string.IsNullOrEmpty(Model.Value?.Hash))
|
||||||
return ReplayAvailability.Online;
|
return ReplayAvailability.Online;
|
||||||
|
|
||||||
return ReplayAvailability.NotAvailable;
|
return ReplayAvailability.NotAvailable;
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -10,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Backgrounds;
|
using osu.Game.Screens.Backgrounds;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -17,7 +20,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Ranking
|
namespace osu.Game.Screens.Ranking
|
||||||
{
|
{
|
||||||
public class ResultsScreen : OsuScreen
|
public abstract class ResultsScreen : OsuScreen
|
||||||
{
|
{
|
||||||
protected const float BACKGROUND_BLUR = 20;
|
protected const float BACKGROUND_BLUR = 20;
|
||||||
|
|
||||||
@ -28,19 +31,26 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
|
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
|
||||||
|
|
||||||
|
public readonly Bindable<ScoreInfo> SelectedScore = new Bindable<ScoreInfo>();
|
||||||
|
|
||||||
|
public readonly ScoreInfo Score;
|
||||||
|
private readonly bool allowRetry;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private Player player { get; set; }
|
private Player player { get; set; }
|
||||||
|
|
||||||
public readonly ScoreInfo Score;
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; }
|
||||||
private readonly bool allowRetry;
|
|
||||||
|
|
||||||
private Drawable bottomPanel;
|
private Drawable bottomPanel;
|
||||||
|
private ScorePanelList panels;
|
||||||
|
|
||||||
public ResultsScreen(ScoreInfo score, bool allowRetry = true)
|
protected ResultsScreen(ScoreInfo score, bool allowRetry = true)
|
||||||
{
|
{
|
||||||
Score = score;
|
Score = score;
|
||||||
this.allowRetry = allowRetry;
|
this.allowRetry = allowRetry;
|
||||||
|
|
||||||
|
SelectedScore.Value = score;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -48,47 +58,68 @@ namespace osu.Game.Screens.Ranking
|
|||||||
{
|
{
|
||||||
FillFlowContainer buttons;
|
FillFlowContainer buttons;
|
||||||
|
|
||||||
InternalChildren = new[]
|
InternalChild = new GridContainer
|
||||||
{
|
{
|
||||||
new ResultsScrollContainer
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Content = new[]
|
||||||
{
|
{
|
||||||
Child = new ScorePanel(Score)
|
new Drawable[]
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
new ResultsScrollContainer
|
||||||
Origin = Anchor.Centre,
|
{
|
||||||
State = PanelState.Expanded
|
Child = panels = new ScorePanelList
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
SelectedScore = { BindTarget = SelectedScore }
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
new[]
|
||||||
bottomPanel = new Container
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = TwoLayerButton.SIZE_EXTENDED.Y,
|
|
||||||
Alpha = 0,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new Box
|
bottomPanel = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
Anchor = Anchor.BottomLeft,
|
||||||
Colour = Color4Extensions.FromHex("#333")
|
Origin = Anchor.BottomLeft,
|
||||||
},
|
RelativeSizeAxes = Axes.X,
|
||||||
buttons = new FillFlowContainer
|
Height = TwoLayerButton.SIZE_EXTENDED.Y,
|
||||||
{
|
Alpha = 0,
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Spacing = new Vector2(5),
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new ReplayDownloadButton(Score) { Width = 300 },
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4Extensions.FromHex("#333")
|
||||||
|
},
|
||||||
|
buttons = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new ReplayDownloadButton(null)
|
||||||
|
{
|
||||||
|
Score = { BindTarget = SelectedScore },
|
||||||
|
Width = 300
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
RowDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.AutoSize)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (Score != null)
|
||||||
|
panels.AddScore(Score);
|
||||||
|
|
||||||
if (player != null && allowRetry)
|
if (player != null && allowRetry)
|
||||||
{
|
{
|
||||||
buttons.Add(new RetryButton { Width = 300 });
|
buttons.Add(new RetryButton { Width = 300 });
|
||||||
@ -105,6 +136,27 @@ namespace osu.Game.Screens.Ranking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
var req = FetchScores(scores => Schedule(() =>
|
||||||
|
{
|
||||||
|
foreach (var s in scores)
|
||||||
|
panels.AddScore(s);
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (req != null)
|
||||||
|
api.Queue(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs a fetch/refresh of scores to be displayed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scoresCallback">A callback which should be called when fetching is completed. Scheduling is not required.</param>
|
||||||
|
/// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns>
|
||||||
|
protected virtual APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback) => null;
|
||||||
|
|
||||||
public override void OnEntering(IScreen last)
|
public override void OnEntering(IScreen last)
|
||||||
{
|
{
|
||||||
base.OnEntering(last);
|
base.OnEntering(last);
|
||||||
@ -142,7 +194,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
content.Height = Math.Max(768, DrawHeight);
|
content.Height = Math.Max(768 - TwoLayerButton.SIZE_EXTENDED.Y, DrawHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,9 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Ranking.Contracted;
|
||||||
using osu.Game.Screens.Ranking.Expanded;
|
using osu.Game.Screens.Ranking.Expanded;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -21,12 +23,12 @@ namespace osu.Game.Screens.Ranking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Width of the panel when contracted.
|
/// Width of the panel when contracted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float contracted_width = 160;
|
public const float CONTRACTED_WIDTH = 130;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Height of the panel when contracted.
|
/// Height of the panel when contracted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float contracted_height = 320;
|
private const float contracted_height = 355;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Width of the panel when expanded.
|
/// Width of the panel when expanded.
|
||||||
@ -46,7 +48,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Height of the top layer when the panel is contracted.
|
/// Height of the top layer when the panel is contracted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float contracted_top_layer_height = 40;
|
private const float contracted_top_layer_height = 30;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Duration for the panel to resize into its expanded/contracted size.
|
/// Duration for the panel to resize into its expanded/contracted size.
|
||||||
@ -71,11 +73,12 @@ namespace osu.Game.Screens.Ranking
|
|||||||
private static readonly ColourInfo expanded_top_layer_colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#444"), Color4Extensions.FromHex("#333"));
|
private static readonly ColourInfo expanded_top_layer_colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#444"), Color4Extensions.FromHex("#333"));
|
||||||
private static readonly ColourInfo expanded_middle_layer_colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#555"), Color4Extensions.FromHex("#333"));
|
private static readonly ColourInfo expanded_middle_layer_colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#555"), Color4Extensions.FromHex("#333"));
|
||||||
private static readonly Color4 contracted_top_layer_colour = Color4Extensions.FromHex("#353535");
|
private static readonly Color4 contracted_top_layer_colour = Color4Extensions.FromHex("#353535");
|
||||||
private static readonly Color4 contracted_middle_layer_colour = Color4Extensions.FromHex("#444");
|
private static readonly Color4 contracted_middle_layer_colour = Color4Extensions.FromHex("#353535");
|
||||||
|
|
||||||
public event Action<PanelState> StateChanged;
|
public event Action<PanelState> StateChanged;
|
||||||
|
public readonly ScoreInfo Score;
|
||||||
|
|
||||||
private readonly ScoreInfo score;
|
private Container content;
|
||||||
|
|
||||||
private Container topLayerContainer;
|
private Container topLayerContainer;
|
||||||
private Drawable topLayerBackground;
|
private Drawable topLayerBackground;
|
||||||
@ -89,47 +92,54 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
public ScorePanel(ScoreInfo score)
|
public ScorePanel(ScoreInfo score)
|
||||||
{
|
{
|
||||||
this.score = score;
|
Score = score;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChild = content = new Container
|
||||||
{
|
{
|
||||||
topLayerContainer = new Container
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(40),
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Name = "Top layer",
|
topLayerContainer = new Container
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = 120,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new Container
|
Name = "Top layer",
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Alpha = 0,
|
||||||
|
Height = 120,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
new Container
|
||||||
CornerRadius = 20,
|
{
|
||||||
CornerExponent = 2.5f,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
CornerRadius = 20,
|
||||||
Child = topLayerBackground = new Box { RelativeSizeAxes = Axes.Both }
|
CornerExponent = 2.5f,
|
||||||
},
|
Masking = true,
|
||||||
topLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
|
Child = topLayerBackground = new Box { RelativeSizeAxes = Axes.Both }
|
||||||
}
|
},
|
||||||
},
|
topLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
middleLayerContainer = new Container
|
}
|
||||||
{
|
},
|
||||||
Name = "Middle layer",
|
middleLayerContainer = new Container
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new Container
|
Name = "Middle layer",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
new Container
|
||||||
CornerRadius = 20,
|
{
|
||||||
CornerExponent = 2.5f,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
CornerRadius = 20,
|
||||||
Child = middleLayerBackground = new Box { RelativeSizeAxes = Axes.Both }
|
CornerExponent = 2.5f,
|
||||||
},
|
Masking = true,
|
||||||
middleLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
|
Child = middleLayerBackground = new Box { RelativeSizeAxes = Axes.Both }
|
||||||
|
},
|
||||||
|
middleLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -174,34 +184,40 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
private void updateState()
|
private void updateState()
|
||||||
{
|
{
|
||||||
topLayerContainer.MoveToY(0, resize_duration, Easing.OutQuint);
|
|
||||||
middleLayerContainer.MoveToY(0, resize_duration, Easing.OutQuint);
|
|
||||||
|
|
||||||
topLayerContent?.FadeOut(content_fade_duration).Expire();
|
topLayerContent?.FadeOut(content_fade_duration).Expire();
|
||||||
middleLayerContent?.FadeOut(content_fade_duration).Expire();
|
middleLayerContent?.FadeOut(content_fade_duration).Expire();
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case PanelState.Expanded:
|
case PanelState.Expanded:
|
||||||
this.ResizeTo(new Vector2(EXPANDED_WIDTH, expanded_height), resize_duration, Easing.OutQuint);
|
Size = new Vector2(EXPANDED_WIDTH, expanded_height);
|
||||||
|
|
||||||
topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint);
|
topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint);
|
||||||
middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint);
|
middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint);
|
||||||
|
|
||||||
topLayerContentContainer.Add(middleLayerContent = new ExpandedPanelTopContent(score.User).With(d => d.Alpha = 0));
|
topLayerContentContainer.Add(middleLayerContent = new ExpandedPanelTopContent(Score.User).With(d => d.Alpha = 0));
|
||||||
middleLayerContentContainer.Add(topLayerContent = new ExpandedPanelMiddleContent(score).With(d => d.Alpha = 0));
|
middleLayerContentContainer.Add(topLayerContent = new ExpandedPanelMiddleContent(Score).With(d => d.Alpha = 0));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PanelState.Contracted:
|
case PanelState.Contracted:
|
||||||
this.ResizeTo(new Vector2(contracted_width, contracted_height), resize_duration, Easing.OutQuint);
|
Size = new Vector2(CONTRACTED_WIDTH, contracted_height);
|
||||||
|
|
||||||
topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint);
|
topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint);
|
||||||
middleLayerBackground.FadeColour(contracted_middle_layer_colour, resize_duration, Easing.OutQuint);
|
middleLayerBackground.FadeColour(contracted_middle_layer_colour, resize_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
middleLayerContentContainer.Add(topLayerContent = new ContractedPanelMiddleContent(Score).With(d => d.Alpha = 0));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (BeginDelayedSequence(resize_duration + top_layer_expand_delay, true))
|
content.ResizeTo(Size, resize_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
bool topLayerExpanded = topLayerContainer.Y < 0;
|
||||||
|
|
||||||
|
// If the top layer was already expanded, then we don't need to wait for the resize and can instead transform immediately. This looks better when changing the panel state.
|
||||||
|
using (BeginDelayedSequence(topLayerExpanded ? 0 : resize_duration + top_layer_expand_delay, true))
|
||||||
{
|
{
|
||||||
|
topLayerContainer.FadeIn();
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case PanelState.Expanded:
|
case PanelState.Expanded:
|
||||||
@ -219,5 +235,13 @@ namespace osu.Game.Screens.Ranking
|
|||||||
middleLayerContent?.FadeIn(content_fade_duration);
|
middleLayerContent?.FadeIn(content_fade_duration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e)
|
||||||
|
{
|
||||||
|
if (State == PanelState.Contracted)
|
||||||
|
State = PanelState.Expanded;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
185
osu.Game/Screens/Ranking/ScorePanelList.cs
Normal file
185
osu.Game/Screens/Ranking/ScorePanelList.cs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Ranking
|
||||||
|
{
|
||||||
|
public class ScorePanelList : CompositeDrawable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Normal spacing between all panels.
|
||||||
|
/// </summary>
|
||||||
|
private const float panel_spacing = 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Spacing around both sides of the expanded panel. This is added on top of <see cref="panel_spacing"/>.
|
||||||
|
/// </summary>
|
||||||
|
private const float expanded_panel_spacing = 15;
|
||||||
|
|
||||||
|
public readonly Bindable<ScoreInfo> SelectedScore = new Bindable<ScoreInfo>();
|
||||||
|
|
||||||
|
private readonly Flow flow;
|
||||||
|
private readonly Scroll scroll;
|
||||||
|
private ScorePanel expandedPanel;
|
||||||
|
|
||||||
|
public ScorePanelList()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = scroll = new Scroll
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = flow = new Flow
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(panel_spacing, 0),
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
SelectedScore.BindValueChanged(selectedScoreChanged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a <see cref="ScoreInfo"/> to this list.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="score">The <see cref="ScoreInfo"/> to add.</param>
|
||||||
|
public void AddScore(ScoreInfo score)
|
||||||
|
{
|
||||||
|
flow.Add(new ScorePanel(score)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
}.With(p =>
|
||||||
|
{
|
||||||
|
p.StateChanged += s =>
|
||||||
|
{
|
||||||
|
if (s == PanelState.Expanded)
|
||||||
|
SelectedScore.Value = p.Score;
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (SelectedScore.Value == score)
|
||||||
|
selectedScoreChanged(new ValueChangedEvent<ScoreInfo>(SelectedScore.Value, SelectedScore.Value));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done.
|
||||||
|
// But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel.
|
||||||
|
if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score))
|
||||||
|
{
|
||||||
|
// A somewhat hacky property is used here because we need to:
|
||||||
|
// 1) Scroll after the scroll container's visible range is updated.
|
||||||
|
// 2) Scroll before the scroll container's scroll position is updated.
|
||||||
|
// Without this, we would have a 1-frame positioning error which looks very jarring.
|
||||||
|
scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Brings a <see cref="ScoreInfo"/> to the centre of the screen and expands it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="score">The <see cref="ScoreInfo"/> to present.</param>
|
||||||
|
private void selectedScoreChanged(ValueChangedEvent<ScoreInfo> score)
|
||||||
|
{
|
||||||
|
// Contract the old panel.
|
||||||
|
foreach (var p in flow.Where(p => p.Score == score.OldValue))
|
||||||
|
{
|
||||||
|
p.State = PanelState.Contracted;
|
||||||
|
p.Margin = new MarginPadding();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the panel corresponding to the new score.
|
||||||
|
expandedPanel = flow.SingleOrDefault(p => p.Score == score.NewValue);
|
||||||
|
|
||||||
|
if (expandedPanel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Expand the new panel.
|
||||||
|
expandedPanel.State = PanelState.Expanded;
|
||||||
|
expandedPanel.Margin = new MarginPadding { Horizontal = expanded_panel_spacing };
|
||||||
|
|
||||||
|
// Scroll to the new panel. This is done manually since we need:
|
||||||
|
// 1) To scroll after the scroll container's visible range is updated.
|
||||||
|
// 2) To account for the centre anchor/origins of panels.
|
||||||
|
// In the end, it's easier to compute the scroll position manually.
|
||||||
|
float scrollOffset = flow.GetPanelIndex(expandedPanel.Score) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing);
|
||||||
|
scroll.ScrollTo(scrollOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
float offset = DrawWidth / 2f;
|
||||||
|
|
||||||
|
// Add padding to both sides such that the centre of an expanded panel on either side is in the middle of the screen.
|
||||||
|
|
||||||
|
if (SelectedScore.Value != null)
|
||||||
|
{
|
||||||
|
// The expanded panel has extra padding applied to it, so it needs to be included into the offset.
|
||||||
|
offset -= ScorePanel.EXPANDED_WIDTH / 2f + expanded_panel_spacing;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
offset -= ScorePanel.CONTRACTED_WIDTH / 2f;
|
||||||
|
|
||||||
|
flow.Padding = new MarginPadding { Horizontal = offset };
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Flow : FillFlowContainer<ScorePanel>
|
||||||
|
{
|
||||||
|
public override IEnumerable<Drawable> FlowingChildren => applySorting(AliveInternalChildren);
|
||||||
|
|
||||||
|
public int GetPanelIndex(ScoreInfo score) => applySorting(Children).TakeWhile(s => s.Score != score).Count();
|
||||||
|
|
||||||
|
private IEnumerable<ScorePanel> applySorting(IEnumerable<Drawable> drawables) => drawables.OfType<ScorePanel>()
|
||||||
|
.OrderByDescending(s => s.Score.TotalScore)
|
||||||
|
.ThenBy(s => s.Score.OnlineScoreID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Scroll : OsuScrollContainer
|
||||||
|
{
|
||||||
|
public new float Target => base.Target;
|
||||||
|
|
||||||
|
public Scroll()
|
||||||
|
: base(Direction.Horizontal)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The target that will be scrolled to instantaneously next frame.
|
||||||
|
/// </summary>
|
||||||
|
public float? InstantScrollTarget;
|
||||||
|
|
||||||
|
protected override void UpdateAfterChildren()
|
||||||
|
{
|
||||||
|
if (InstantScrollTarget != null)
|
||||||
|
{
|
||||||
|
ScrollTo(InstantScrollTarget.Value, false);
|
||||||
|
InstantScrollTarget = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.UpdateAfterChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HandlePositionalInput => false;
|
||||||
|
|
||||||
|
public override bool HandleNonPositionalInput => false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
osu.Game/Screens/Ranking/SoloResultsScreen.cs
Normal file
32
osu.Game/Screens/Ranking/SoloResultsScreen.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Ranking
|
||||||
|
{
|
||||||
|
public class SoloResultsScreen : ResultsScreen
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private RulesetStore rulesets { get; set; }
|
||||||
|
|
||||||
|
public SoloResultsScreen(ScoreInfo score, bool allowRetry = true)
|
||||||
|
: base(score, allowRetry)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback)
|
||||||
|
{
|
||||||
|
var req = new GetScoresRequest(Score.Beatmap, Score.Ruleset);
|
||||||
|
req.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineScoreID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets)));
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,7 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void PresentScore(ScoreInfo score) =>
|
protected void PresentScore(ScoreInfo score) =>
|
||||||
FinaliseSelection(score.Beatmap, score.Ruleset, () => this.Push(new ResultsScreen(score)));
|
FinaliseSelection(score.Beatmap, score.Ruleset, () => this.Push(new SoloResultsScreen(score)));
|
||||||
|
|
||||||
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();
|
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();
|
||||||
|
|
||||||
|
52
osu.Game/Tests/TestScoreInfo.cs
Normal file
52
osu.Game/Tests/TestScoreInfo.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests
|
||||||
|
{
|
||||||
|
public class TestScoreInfo : ScoreInfo
|
||||||
|
{
|
||||||
|
public TestScoreInfo(RulesetInfo ruleset)
|
||||||
|
{
|
||||||
|
User = new User
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
Username = "peppy",
|
||||||
|
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
|
};
|
||||||
|
|
||||||
|
Beatmap = new TestBeatmap(ruleset).BeatmapInfo;
|
||||||
|
Ruleset = ruleset;
|
||||||
|
RulesetID = ruleset.ID ?? 0;
|
||||||
|
Mods = new Mod[] { new TestModHardRock(), new TestModDoubleTime() };
|
||||||
|
|
||||||
|
TotalScore = 2845370;
|
||||||
|
Accuracy = 0.95;
|
||||||
|
MaxCombo = 999;
|
||||||
|
Rank = ScoreRank.S;
|
||||||
|
Date = DateTimeOffset.Now;
|
||||||
|
|
||||||
|
Statistics[HitResult.Miss] = 1;
|
||||||
|
Statistics[HitResult.Meh] = 50;
|
||||||
|
Statistics[HitResult.Good] = 100;
|
||||||
|
Statistics[HitResult.Great] = 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestModHardRock : ModHardRock
|
||||||
|
{
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestModDoubleTime : ModDoubleTime
|
||||||
|
{
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user