diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs new file mode 100644 index 0000000000..fe94165777 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs @@ -0,0 +1,95 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Overlays.BeatmapSet; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneBeatmapAvailability : OsuTestScene + { + private readonly BeatmapAvailability container; + + public TestSceneBeatmapAvailability() + { + Add(container = new BeatmapAvailability()); + } + + [Test] + public void TestUndownloadableWithLink() + { + AddStep("set undownloadable beatmapset with link", () => container.BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = true, + ExternalLink = @"https://osu.ppy.sh", + }, + }, + }); + + visiblityAssert(true); + } + + [Test] + public void TestUndownloadableNoLink() + { + AddStep("set undownloadable beatmapset without link", () => container.BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = true, + }, + }, + }); + + visiblityAssert(true); + } + + [Test] + public void TestPartsRemovedWithLink() + { + AddStep("set parts-removed beatmapset with link", () => container.BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = false, + ExternalLink = @"https://osu.ppy.sh", + }, + }, + }); + + visiblityAssert(true); + } + + [Test] + public void TestNormal() + { + AddStep("set normal beatmapset", () => container.BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = false, + }, + }, + }); + + visiblityAssert(false); + } + + private void visiblityAssert(bool shown) + { + AddAssert($"is container {(shown ? "visible" : "hidden")}", () => container.Alpha == (shown ? 1 : 0)); + } + } +} diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index fb2d4efc68..c494f5ef33 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Online [TestFixture] public class TestSceneBeatmapSetOverlay : OsuTestScene { - private readonly BeatmapSetOverlay overlay; + private readonly TestBeatmapSetOverlay overlay; public override IReadOnlyList RequiredTypes => new[] { @@ -39,21 +39,22 @@ namespace osu.Game.Tests.Visual.Online typeof(Info), typeof(PreviewButton), typeof(SuccessRate), + typeof(BeatmapAvailability), }; - private RulesetInfo maniaRuleset; private RulesetInfo taikoRuleset; + private RulesetInfo maniaRuleset; public TestSceneBeatmapSetOverlay() { - Add(overlay = new BeatmapSetOverlay()); + Add(overlay = new TestBeatmapSetOverlay()); } [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - maniaRuleset = rulesets.GetRuleset(3); taikoRuleset = rulesets.GetRuleset(1); + maniaRuleset = rulesets.GetRuleset(3); } [Test] @@ -75,159 +76,53 @@ namespace osu.Game.Tests.Visual.Online { overlay.ShowBeatmapSet(new BeatmapSetInfo { + OnlineBeatmapSetID = 1235, Metadata = new BeatmapMetadata { - Title = @"Lachryma ", - Artist = @"Kaneko Chiharu", - Source = @"SOUND VOLTEX III GRAVITY WARS", - Tags = @"sdvx grace the 5th kac original song contest konami bemani", + Title = @"an awesome beatmap", + Artist = @"naru narusegawa", + Source = @"hinata sou", + Tags = @"test tag tag more tag", Author = new User { - Username = @"Fresh Chicken", - Id = 3984370, + Username = @"BanchoBot", + Id = 3, }, }, OnlineInfo = new BeatmapSetOnlineInfo { - Preview = @"https://b.ppy.sh/preview/415886.mp3", - PlayCount = 681380, - FavouriteCount = 356, - Submitted = new DateTime(2016, 2, 10), - Ranked = new DateTime(2016, 6, 19), - Status = BeatmapSetOnlineStatus.Ranked, - BPM = 236, + Preview = @"https://b.ppy.sh/preview/12345.mp3", + PlayCount = 123, + FavouriteCount = 456, + Submitted = DateTime.Now, + Ranked = DateTime.Now, + BPM = 111, HasVideo = true, - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778", - }, + HasStoryboard = true, + Covers = new BeatmapSetOnlineCovers(), }, Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List { new BeatmapInfo { - StarDifficulty = 1.36, - Version = @"BASIC", + StarDifficulty = 9.99, + Version = @"TEST", Ruleset = maniaRuleset, BaseDifficulty = new BeatmapDifficulty { - CircleSize = 4, - DrainRate = 6.5f, - OverallDifficulty = 6.5f, - ApproachRate = 5, + CircleSize = 1, + DrainRate = 2.3f, + OverallDifficulty = 4.5f, + ApproachRate = 6, }, OnlineInfo = new BeatmapOnlineInfo { - Length = 115000, - CircleCount = 265, - SliderCount = 71, - PlayCount = 47906, - PassCount = 19899, - }, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, - }, - new BeatmapInfo - { - StarDifficulty = 2.22, - Version = @"NOVICE", - Ruleset = maniaRuleset, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 4, - DrainRate = 7, - OverallDifficulty = 7, - ApproachRate = 5, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 118000, - CircleCount = 592, - SliderCount = 62, - PlayCount = 162021, - PassCount = 72116, - }, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, - }, - new BeatmapInfo - { - StarDifficulty = 3.49, - Version = @"ADVANCED", - Ruleset = maniaRuleset, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 4, - DrainRate = 7.5f, - OverallDifficulty = 7.5f, - ApproachRate = 5, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 118000, - CircleCount = 1042, - SliderCount = 79, - PlayCount = 225178, - PassCount = 73001, - }, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, - }, - new BeatmapInfo - { - StarDifficulty = 4.24, - Version = @"EXHAUST", - Ruleset = maniaRuleset, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 4, - DrainRate = 8, - OverallDifficulty = 8, - ApproachRate = 5, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 118000, - CircleCount = 1352, - SliderCount = 69, - PlayCount = 131545, - PassCount = 42703, - }, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, - }, - new BeatmapInfo - { - StarDifficulty = 5.26, - Version = @"GRAVITY", - Ruleset = maniaRuleset, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 4, - DrainRate = 8.5f, - OverallDifficulty = 8.5f, - ApproachRate = 5, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 118000, - CircleCount = 1730, - SliderCount = 115, - PlayCount = 117673, - PassCount = 24241, + Length = 456000, + CircleCount = 111, + SliderCount = 12, + PlayCount = 222, + PassCount = 21, }, Metrics = new BeatmapMetrics { @@ -239,162 +134,68 @@ namespace osu.Game.Tests.Visual.Online }); }); - AddStep(@"show second", () => + downloadAssert(true); + } + + [Test] + public void TestAvailability() + { + AddStep(@"show undownloadable", () => { overlay.ShowBeatmapSet(new BeatmapSetInfo { + OnlineBeatmapSetID = 1234, Metadata = new BeatmapMetadata { - Title = @"Soumatou Labyrinth", - Artist = @"Yunomi with Momobako&miko", - Tags = @"mmbk.com yuzu__rinrin charlotte", + Title = @"undownloadable beatmap", + Artist = @"no one", + Source = @"some source", + Tags = @"another test tag tag more test tags", Author = new User { - Username = @"komasy", - Id = 1980256, + Username = @"BanchoBot", + Id = 3, }, }, OnlineInfo = new BeatmapSetOnlineInfo { - Preview = @"https://b.ppy.sh/preview/625493.mp3", - PlayCount = 22996, - FavouriteCount = 58, - Submitted = new DateTime(2016, 6, 11), - Ranked = new DateTime(2016, 7, 12), - Status = BeatmapSetOnlineStatus.Pending, - BPM = 160, - HasVideo = false, - Covers = new BeatmapSetOnlineCovers + Availability = new BeatmapSetOnlineAvailability { - Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472", + DownloadDisabled = true, + ExternalLink = "https://osu.ppy.sh", }, + Preview = @"https://b.ppy.sh/preview/1234.mp3", + PlayCount = 123, + FavouriteCount = 456, + Submitted = DateTime.Now, + Ranked = DateTime.Now, + BPM = 111, + HasVideo = true, + HasStoryboard = true, + Covers = new BeatmapSetOnlineCovers(), }, Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List { new BeatmapInfo { - StarDifficulty = 1.40, - Version = @"yzrin's Kantan", + StarDifficulty = 5.67, + Version = @"ANOTHER TEST", Ruleset = taikoRuleset, BaseDifficulty = new BeatmapDifficulty { - CircleSize = 2, - DrainRate = 7, - OverallDifficulty = 3, - ApproachRate = 10, + CircleSize = 9, + DrainRate = 8, + OverallDifficulty = 7, + ApproachRate = 6, }, OnlineInfo = new BeatmapOnlineInfo { - Length = 193000, - CircleCount = 262, - SliderCount = 0, - PlayCount = 3952, - PassCount = 1373, - }, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, - }, - new BeatmapInfo - { - StarDifficulty = 2.23, - Version = @"Futsuu", - Ruleset = taikoRuleset, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 2, - DrainRate = 6, - OverallDifficulty = 4, - ApproachRate = 10, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 193000, - CircleCount = 464, - SliderCount = 0, - PlayCount = 4833, - PassCount = 920, - }, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, - }, - new BeatmapInfo - { - StarDifficulty = 3.19, - Version = @"Muzukashii", - Ruleset = taikoRuleset, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 2, - DrainRate = 6, - OverallDifficulty = 5, - ApproachRate = 10, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 193000, - CircleCount = 712, - SliderCount = 0, - PlayCount = 4405, - PassCount = 854, - }, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, - }, - new BeatmapInfo - { - StarDifficulty = 3.97, - Version = @"Charlotte's Oni", - Ruleset = taikoRuleset, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 5, - DrainRate = 6, - OverallDifficulty = 5.5f, - ApproachRate = 10, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 193000, - CircleCount = 943, - SliderCount = 0, - PlayCount = 3950, - PassCount = 693, - }, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, - }, - new BeatmapInfo - { - StarDifficulty = 5.08, - Version = @"Labyrinth Oni", - Ruleset = taikoRuleset, - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 5, - DrainRate = 5, - OverallDifficulty = 6, - ApproachRate = 10, - }, - OnlineInfo = new BeatmapOnlineInfo - { - Length = 193000, - CircleCount = 1068, - SliderCount = 0, - PlayCount = 5856, - PassCount = 1207, + Length = 123000, + CircleCount = 123, + SliderCount = 45, + PlayCount = 567, + PassCount = 89, }, Metrics = new BeatmapMetrics { @@ -405,6 +206,8 @@ namespace osu.Game.Tests.Visual.Online }, }); }); + + downloadAssert(false); } [Test] @@ -418,5 +221,15 @@ namespace osu.Game.Tests.Visual.Online { AddStep(@"show without reload", overlay.Show); } + + private void downloadAssert(bool shown) + { + AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.DownloadButtonsVisible == shown); + } + + private class TestBeatmapSetOverlay : BeatmapSetOverlay + { + public bool DownloadButtonsVisible => Header.DownloadButtonsVisible; + } } } diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs new file mode 100644 index 0000000000..ceb19a3ec6 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs @@ -0,0 +1,94 @@ +// 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 NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Overlays.Direct; +using osu.Game.Rulesets.Osu; +using osuTK; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneDirectDownloadButton : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(DownloadButton) + }; + + private TestDownloadButton downloadButton; + + [Test] + public void TestDownloadableBeatmap() + { + createButton(true); + assertEnabled(true); + } + + [Test] + public void TestUndownloadableBeatmap() + { + createButton(false); + assertEnabled(false); + } + + private void assertEnabled(bool enabled) + { + AddAssert($"button {(enabled ? "enabled" : "disabled")}", () => downloadButton.DownloadEnabled == enabled); + } + + private void createButton(bool downloadable) + { + AddStep("create button", () => + { + Child = downloadButton = new TestDownloadButton(downloadable ? getDownloadableBeatmapSet() : getUndownloadableBeatmapSet()) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(75, 50), + }; + }); + } + + private BeatmapSetInfo getDownloadableBeatmapSet() + { + var normal = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo; + normal.OnlineInfo.HasVideo = true; + normal.OnlineInfo.HasStoryboard = true; + + return normal; + } + + private BeatmapSetInfo getUndownloadableBeatmapSet() + { + var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo; + beatmap.Metadata.Artist = "test"; + beatmap.Metadata.Title = "undownloadable"; + beatmap.Metadata.AuthorString = "test"; + + beatmap.OnlineInfo.HasVideo = true; + beatmap.OnlineInfo.HasStoryboard = true; + + beatmap.OnlineInfo.Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = true, + ExternalLink = "http://osu.ppy.sh", + }; + + return beatmap; + } + + private class TestDownloadButton : DownloadButton + { + public new bool DownloadEnabled => base.DownloadEnabled; + + public TestDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) + : base(beatmapSet, noVideo) + { + } + } + } +} diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index 8b67892fbb..53dbaeddda 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -6,8 +6,11 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; using osu.Game.Overlays.Direct; +using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; +using osu.Game.Users; using osuTK; namespace osu.Game.Tests.Visual.Online @@ -21,25 +24,74 @@ namespace osu.Game.Tests.Visual.Online typeof(IconPill) }; + private BeatmapSetInfo getUndownloadableBeatmapSet(RulesetInfo ruleset) => new BeatmapSetInfo + { + OnlineBeatmapSetID = 123, + Metadata = new BeatmapMetadata + { + Title = "undownloadable beatmap", + Artist = "test", + Source = "more tests", + Author = new User + { + Username = "BanchoBot", + Id = 3, + }, + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = true, + }, + Preview = @"https://b.ppy.sh/preview/12345.mp3", + PlayCount = 123, + FavouriteCount = 456, + BPM = 111, + HasVideo = true, + HasStoryboard = true, + Covers = new BeatmapSetOnlineCovers(), + }, + Beatmaps = new List + { + new BeatmapInfo + { + Ruleset = ruleset, + Version = "Test", + StarDifficulty = 6.42, + } + } + }; + [BackgroundDependencyLoader] private void load() { - var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true; - beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true; + var ruleset = new OsuRuleset().RulesetInfo; - Child = new FillFlowContainer + var normal = CreateWorkingBeatmap(ruleset).BeatmapSetInfo; + normal.OnlineInfo.HasVideo = true; + normal.OnlineInfo.HasStoryboard = true; + + var undownloadable = getUndownloadableBeatmapSet(ruleset); + + Child = new BasicScrollContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Padding = new MarginPadding(20), - Spacing = new Vector2(0, 20), - Children = new Drawable[] + RelativeSizeAxes = Axes.Both, + Child = new FillFlowContainer { - new DirectGridPanel(beatmap.BeatmapSetInfo), - new DirectListPanel(beatmap.BeatmapSetInfo) - } + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = new MarginPadding(20), + Spacing = new Vector2(0, 20), + Children = new Drawable[] + { + new DirectGridPanel(normal), + new DirectListPanel(normal), + new DirectGridPanel(undownloadable), + new DirectListPanel(undownloadable), + }, + }, }; } } diff --git a/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs new file mode 100644 index 0000000000..c344cb9598 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneProfileRulesetSelector.cs @@ -0,0 +1,40 @@ +// 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.Game.Overlays.Profile.Header.Components; +using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Taiko; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneProfileRulesetSelector : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ProfileRulesetSelector), + typeof(ProfileRulesetTabItem), + }; + + public TestSceneProfileRulesetSelector() + { + ProfileRulesetSelector selector; + + Child = selector = new ProfileRulesetSelector + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + + AddStep("set osu! as default", () => selector.SetDefaultRuleset(new OsuRuleset().RulesetInfo)); + AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo)); + AddStep("set taiko as default", () => selector.SetDefaultRuleset(new TaikoRuleset().RulesetInfo)); + AddStep("set catch as default", () => selector.SetDefaultRuleset(new CatchRuleset().RulesetInfo)); + AddStep("select default ruleset", selector.SelectDefaultRuleset); + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs index 0ccc9a924c..ea3f0b61b9 100644 --- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs @@ -65,6 +65,11 @@ namespace osu.Game.Beatmaps /// The amount of people who have favourited this beatmap set. /// public int FavouriteCount { get; set; } + + /// + /// The availability of this beatmap set. + /// + public BeatmapSetOnlineAvailability Availability { get; set; } } public class BeatmapSetOnlineCovers @@ -84,4 +89,13 @@ namespace osu.Game.Beatmaps [JsonProperty(@"list@2x")] public string List { get; set; } } + + public class BeatmapSetOnlineAvailability + { + [JsonProperty(@"download_disabled")] + public bool DownloadDisabled { get; set; } + + [JsonProperty(@"more_information")] + public string ExternalLink { get; set; } + } } diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs index c7c4c1fb1e..16eecb7198 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapSetCover.cs @@ -2,9 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osuTK.Graphics; +using osu.Game.Graphics; namespace osu.Game.Beatmaps.Drawables { @@ -49,7 +50,7 @@ namespace osu.Game.Beatmaps.Drawables Child = new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, + Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.1f)), }; } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 00e08633dd..200a705500 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -63,6 +63,9 @@ namespace osu.Game.Online.API.Requests.Responses set => Author.Id = value; } + [JsonProperty(@"availability")] + private BeatmapSetOnlineAvailability availability { get; set; } + [JsonProperty(@"beatmaps")] private IEnumerable beatmaps { get; set; } @@ -87,6 +90,7 @@ namespace osu.Game.Online.API.Requests.Responses Submitted = submitted, Ranked = ranked, LastUpdated = lastUpdated, + Availability = availability, }, Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(), }; diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs b/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs new file mode 100644 index 0000000000..896c646552 --- /dev/null +++ b/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs @@ -0,0 +1,83 @@ +// 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.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osuTK.Graphics; + +namespace osu.Game.Overlays.BeatmapSet +{ + public class BeatmapAvailability : Container + { + private BeatmapSetInfo beatmapSet; + + private bool downloadDisabled => BeatmapSet?.OnlineInfo.Availability?.DownloadDisabled ?? false; + private bool hasExternalLink => !string.IsNullOrEmpty(BeatmapSet?.OnlineInfo.Availability?.ExternalLink); + + private readonly LinkFlowContainer textContainer; + + public BeatmapAvailability() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding { Top = 10, Right = 20 }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.6f), + }, + textContainer = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 14)) + { + Direction = FillDirection.Full, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding(10), + }, + }; + } + + public BeatmapSetInfo BeatmapSet + { + get => beatmapSet; + + set + { + if (value == beatmapSet) + return; + + beatmapSet = value; + + if (downloadDisabled || hasExternalLink) + { + Show(); + updateText(); + } + else + Hide(); + } + } + + private void updateText() + { + textContainer.Clear(); + textContainer.AddParagraph(downloadDisabled + ? "This beatmap is currently not available for download." + : "Portions of this beatmap have been removed at the request of the creator or a third-party rights holder.", t => t.Colour = Color4.Orange); + + if (hasExternalLink) + { + textContainer.NewParagraph(); + textContainer.NewParagraph(); + textContainer.AddLink("Check here for more information.", BeatmapSet.OnlineInfo.Availability.ExternalLink, creationParameters: t => t.Font = OsuFont.GetFont(size: 10)); + } + } + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 6fe9e43d2c..1c1167d08e 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.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.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -33,9 +34,12 @@ namespace osu.Game.Overlays.BeatmapSet private readonly OsuSpriteText title, artist; private readonly AuthorInfo author; private readonly FillFlowContainer downloadButtonsContainer; + private readonly BeatmapAvailability beatmapAvailability; private readonly BeatmapSetOnlineStatusPill onlineStatusPill; public Details Details; + public bool DownloadButtonsVisible => downloadButtonsContainer.Any(); + public readonly BeatmapPicker Picker; private readonly FavouriteButton favouriteButton; @@ -100,7 +104,8 @@ namespace osu.Game.Overlays.BeatmapSet }, new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Top = 20, @@ -149,6 +154,7 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Top = 20 }, Child = author = new AuthorInfo(), }, + beatmapAvailability = new BeatmapAvailability(), new Container { RelativeSizeAxes = Axes.X, @@ -167,13 +173,14 @@ namespace osu.Game.Overlays.BeatmapSet }, }, }, - loading = new LoadingAnimation - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } } }, + loading = new LoadingAnimation + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(1.5f), + }, new FillFlowContainer { Anchor = Anchor.BottomRight, @@ -214,7 +221,7 @@ namespace osu.Game.Overlays.BeatmapSet BeatmapSet.BindValueChanged(setInfo => { - Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; + Picker.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; cover.BeatmapSet = setInfo.NewValue; if (setInfo.NewValue == null) @@ -251,11 +258,17 @@ namespace osu.Game.Overlays.BeatmapSet { if (BeatmapSet.Value == null) return; + if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) + { + downloadButtonsContainer.Clear(); + return; + } + switch (State.Value) { case DownloadState.LocallyAvailable: // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet.Value) + downloadButtonsContainer.Child = new Direct.DownloadButton(BeatmapSet.Value) { Width = 50, RelativeSizeAxes = Axes.Y diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 205909ce7d..19f6a3f692 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -27,12 +27,10 @@ namespace osu.Game.Overlays public const float TOP_PADDING = 25; public const float RIGHT_WIDTH = 275; - private readonly Header header; + protected readonly Header Header; private RulesetStore rulesets; - private readonly OsuScrollContainer scroll; - private readonly Bindable beatmapSet = new Bindable(); // receive input outside our bounds so we can trigger a close event on ourselves. @@ -40,6 +38,7 @@ namespace osu.Game.Overlays public BeatmapSetOverlay() { + OsuScrollContainer scroll; Info info; ScoresContainer scores; @@ -61,7 +60,7 @@ namespace osu.Game.Overlays Direction = FillDirection.Vertical, Children = new Drawable[] { - header = new Header(), + Header = new Header(), info = new Info(), scores = new ScoresContainer(), }, @@ -69,13 +68,15 @@ namespace osu.Game.Overlays }, }; - header.BeatmapSet.BindTo(beatmapSet); + Header.BeatmapSet.BindTo(beatmapSet); info.BeatmapSet.BindTo(beatmapSet); - header.Picker.Beatmap.ValueChanged += b => + Header.Picker.Beatmap.ValueChanged += b => { info.Beatmap = b.NewValue; scores.Beatmap = b.NewValue; + + scroll.ScrollToStart(); }; } @@ -104,7 +105,7 @@ namespace osu.Game.Overlays req.Success += res => { beatmapSet.Value = res.ToBeatmapSet(rulesets); - header.Picker.Beatmap.Value = header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); + Header.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); }; API.Queue(req); Show(); @@ -119,11 +120,14 @@ namespace osu.Game.Overlays Show(); } + /// + /// Show an already fully-populated beatmap set. + /// + /// The set to show. public void ShowBeatmapSet(BeatmapSetInfo set) { beatmapSet.Value = set; Show(); - scroll.ScrollTo(0); } } } diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index b731e95d54..6f3b5bc5f1 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -27,6 +27,7 @@ namespace osu.Game.Overlays.Direct private const float height = 70; private FillFlowContainer statusContainer; + protected DownloadButton DownloadButton; private PlayButton playButton; private Box progressBar; @@ -149,7 +150,7 @@ namespace osu.Game.Overlays.Direct Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, - Child = new DownloadButton(SetInfo) + Child = DownloadButton = new DownloadButton(SetInfo) { Size = new Vector2(height - vertical_padding * 3), Margin = new MarginPadding { Left = vertical_padding * 2, Right = vertical_padding }, diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index f413dc3771..8199d80528 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -34,6 +35,7 @@ namespace osu.Game.Overlays.Direct public PreviewTrack Preview => PlayButton.Preview; public Bindable PreviewPlaying => PlayButton.Playing; + protected abstract PlayButton PlayButton { get; } protected abstract Box PreviewBar { get; } @@ -43,6 +45,8 @@ namespace osu.Game.Overlays.Direct protected DirectPanel(BeatmapSetInfo setInfo) { + Debug.Assert(setInfo.OnlineBeatmapSetID != null); + SetInfo = setInfo; } @@ -117,12 +121,11 @@ namespace osu.Game.Overlays.Direct protected override bool OnClick(ClickEvent e) { - ShowInformation(); + Debug.Assert(SetInfo.OnlineBeatmapSetID != null); + beatmapSetOverlay?.FetchAndShowBeatmapSet(SetInfo.OnlineBeatmapSetID.Value); return true; } - protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo); - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index 348ce427bd..81709187e7 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -16,15 +16,15 @@ namespace osu.Game.Overlays.Direct { public class DownloadButton : BeatmapDownloadTrackingComposite { + protected bool DownloadEnabled => button.Enabled.Value; + private readonly bool noVideo; private readonly SpriteIcon icon; private readonly SpriteIcon checkmark; private readonly Box background; private OsuColour colours; - private readonly ShakeContainer shakeContainer; - private readonly OsuAnimatedButton button; public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) @@ -73,11 +73,18 @@ namespace osu.Game.Overlays.Direct FinishTransforms(true); } - [BackgroundDependencyLoader(permitNulls: true)] + [BackgroundDependencyLoader(true)] private void load(OsuColour colours, OsuGame game, BeatmapManager beatmaps) { this.colours = colours; + if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) + { + button.Enabled.Value = false; + button.TooltipText = "This beatmap is currently not available for download."; + return; + } + button.Action = () => { switch (State.Value) diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs new file mode 100644 index 0000000000..b6112a6501 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetSelector.cs @@ -0,0 +1,66 @@ +// 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.UserInterface; +using osu.Game.Graphics; +using osu.Game.Rulesets; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile.Header.Components +{ + public class ProfileRulesetSelector : RulesetSelector + { + private Color4 accentColour = Color4.White; + + public ProfileRulesetSelector() + { + TabContainer.Masking = false; + TabContainer.Spacing = new Vector2(10, 0); + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + accentColour = colours.Seafoam; + + foreach (TabItem tabItem in TabContainer) + ((ProfileRulesetTabItem)tabItem).AccentColour = accentColour; + } + + public void SetDefaultRuleset(RulesetInfo ruleset) + { + // Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value + foreach (TabItem tabItem in TabContainer) + ((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID; + } + + public void SelectDefaultRuleset() + { + // Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value + foreach (TabItem tabItem in TabContainer) + { + if (((ProfileRulesetTabItem)tabItem).IsDefault) + { + Current.Value = ((ProfileRulesetTabItem)tabItem).Value; + return; + } + } + } + + protected override TabItem CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value) + { + AccentColour = accentColour + }; + + protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + }; + } +} diff --git a/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs new file mode 100644 index 0000000000..b5170ea3a2 --- /dev/null +++ b/osu.Game/Overlays/Profile/Header/Components/ProfileRulesetTabItem.cs @@ -0,0 +1,125 @@ +// 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.Sprites; +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 osu.Game.Rulesets; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Profile.Header.Components +{ + public class ProfileRulesetTabItem : TabItem, IHasAccentColour + { + private readonly OsuSpriteText text; + private readonly SpriteIcon icon; + + private Color4 accentColour; + + public Color4 AccentColour + { + get => accentColour; + set + { + if (accentColour == value) + return; + + accentColour = value; + + updateState(); + } + } + + private bool isDefault; + + public bool IsDefault + { + get => isDefault; + set + { + if (isDefault == value) + return; + + isDefault = value; + + icon.FadeTo(isDefault ? 1 : 0, 200, Easing.OutQuint); + } + } + + public ProfileRulesetTabItem(RulesetInfo value) + : base(value) + { + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3, 0), + Children = new Drawable[] + { + text = new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Text = value.Name, + }, + icon = new SpriteIcon + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Alpha = 0, + AlwaysPresent = true, + Icon = FontAwesome.Solid.Star, + Size = new Vector2(12), + }, + } + }, + 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() + { + text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium); + + if (IsHovered || Active.Value) + { + text.FadeColour(Color4.White, 120, Easing.InQuad); + icon.FadeColour(Color4.White, 120, Easing.InQuad); + } + else + { + text.FadeColour(AccentColour, 120, Easing.InQuad); + icon.FadeColour(AccentColour, 120, Easing.InQuad); + } + } + } +} diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 058d84d32f..b924b3302f 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -32,7 +32,8 @@ namespace osu.Game.Overlays public void ShowUser(User user, bool fetchOnline = true) { - if (user == User.SYSTEM_USER) return; + if (user == User.SYSTEM_USER) + return; Show(); @@ -76,7 +77,7 @@ namespace osu.Game.Overlays { Colour = OsuColour.Gray(34), RelativeSizeAxes = Axes.Both - } + }, }); sectionsContainer.SelectedSection.ValueChanged += section => {