diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index 8377b3786a..e2465d727e 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Catch.Mods { public class CatchModDifficultyAdjust : ModDifficultyAdjust { - [SettingSource("Fruit Size", "Override a beatmap's set CS.")] + [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)] public BindableNumber CircleSize { get; } = new BindableFloat { Precision = 0.1f, @@ -20,7 +20,7 @@ public class CatchModDifficultyAdjust : ModDifficultyAdjust Value = 5, }; - [SettingSource("Approach Rate", "Override a beatmap's set AR.")] + [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)] public BindableNumber ApproachRate { get; } = new BindableFloat { Precision = 0.1f, diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 7eee71be81..75de6896a3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModDifficultyAdjust : ModDifficultyAdjust { - [SettingSource("Circle Size", "Override a beatmap's set CS.")] + [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)] public BindableNumber CircleSize { get; } = new BindableFloat { Precision = 0.1f, @@ -20,7 +20,7 @@ public class OsuModDifficultyAdjust : ModDifficultyAdjust Value = 5, }; - [SettingSource("Approach Rate", "Override a beatmap's set AR.")] + [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)] public BindableNumber ApproachRate { get; } = new BindableFloat { Precision = 0.1f, diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs index 990e0a166b..dea1e710b5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs @@ -5,9 +5,11 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; using osu.Game.Screens.Select.Details; @@ -22,6 +24,9 @@ public class TestSceneBeatmapSetOverlayDetails : OsuTestScene private RatingsExposingDetails details; + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + [SetUp] public void Setup() => Schedule(() => { @@ -55,8 +60,12 @@ public void TestMetrics() { Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), - } + }, } + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Status = BeatmapSetOnlineStatus.Ranked } }; } diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs index 2b572c1f6c..03003daf81 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs @@ -5,11 +5,13 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; using osu.Game.Screens.Select.Details; using osuTK; @@ -26,6 +28,9 @@ public class TestSceneBeatmapSetOverlaySuccessRate : OsuTestScene private GraphExposingSuccessRate successRate; + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + [SetUp] public void Setup() => Schedule(() => { diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs index cc3b2ac68b..f9a7bc99c3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardScopeSelector.cs @@ -7,11 +7,16 @@ using osu.Framework.Graphics; using osu.Framework.Bindables; using osu.Game.Screens.Select.Leaderboards; +using osu.Framework.Allocation; +using osu.Game.Overlays; namespace osu.Game.Tests.Visual.Online { public class TestSceneLeaderboardScopeSelector : OsuTestScene { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); + public override IReadOnlyList RequiredTypes => new[] { typeof(LeaderboardScopeSelector), diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs index 898e461bde..1e711b3cd7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsHeader.cs @@ -34,25 +34,7 @@ public TestSceneRankingsHeader() { Current = { BindTarget = scope }, Country = { BindTarget = countryBindable }, - Ruleset = { BindTarget = ruleset }, - Spotlights = new[] - { - new Spotlight - { - Id = 1, - Text = "Spotlight 1" - }, - new Spotlight - { - Id = 2, - Text = "Spotlight 2" - }, - new Spotlight - { - Id = 3, - Text = "Spotlight 3" - } - } + Ruleset = { BindTarget = ruleset } }); var country = new Country diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankingsSpotlightSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneRankingsSpotlightSelector.cs index e46c8a4a71..f27ab1e775 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankingsSpotlightSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankingsSpotlightSelector.cs @@ -35,6 +35,12 @@ public TestSceneRankingsSpotlightSelector() Add(selector = new SpotlightSelector()); } + [Test] + public void TestVisibility() + { + AddStep("Toggle Visibility", selector.ToggleVisibility); + } + [Test] public void TestLocalSpotlights() { diff --git a/osu.Game.Tests/Visual/Online/TestSceneSpotlightsLayout.cs b/osu.Game.Tests/Visual/Online/TestSceneSpotlightsLayout.cs new file mode 100644 index 0000000000..d025a8d7c2 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneSpotlightsLayout.cs @@ -0,0 +1,55 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays; +using osu.Game.Overlays.Rankings; +using osu.Game.Rulesets; +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 TestSceneSpotlightsLayout : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(SpotlightsLayout), + typeof(SpotlightSelector), + }; + + protected override bool UseOnlineAPI => true; + + [Cached] + private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Green); + + public TestSceneSpotlightsLayout() + { + var ruleset = new Bindable(new OsuRuleset().RulesetInfo); + + Add(new BasicScrollContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Width = 0.8f, + Child = new SpotlightsLayout + { + Ruleset = { BindTarget = ruleset } + } + }); + + AddStep("Osu ruleset", () => ruleset.Value = new OsuRuleset().RulesetInfo); + AddStep("Mania ruleset", () => ruleset.Value = new ManiaRuleset().RulesetInfo); + AddStep("Taiko ruleset", () => ruleset.Value = new TaikoRuleset().RulesetInfo); + AddStep("Catch ruleset", () => ruleset.Value = new CatchRuleset().RulesetInfo); + } + } +} diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 80192b9ebc..9474c08c5a 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -17,6 +17,7 @@ using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -426,6 +427,44 @@ public void TestStartAfterUnMatchingFilterDoesNotStart() AddAssert("start not requested", () => !startRequested); } + [TestCase(false)] + [TestCase(true)] + public void TestExternalBeatmapChangeWhileFiltered(bool differentRuleset) + { + createSongSelect(); + addManyTestMaps(); + + changeRuleset(0); + + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null); + + AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = "nonono"); + + AddUntilStep("dummy selected", () => Beatmap.Value is DummyWorkingBeatmap); + + AddUntilStep("has no selection", () => songSelect.Carousel.SelectedBeatmap == null); + + BeatmapInfo target = null; + + AddStep("select beatmap externally", () => + { + target = manager.GetAllUsableBeatmapSets().Where(b => b.Beatmaps.Any(bi => bi.RulesetID == (differentRuleset ? 1 : 0))) + .ElementAt(5).Beatmaps.First(); + + Beatmap.Value = manager.GetWorkingBeatmap(target); + }); + + AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null); + + AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmap?.OnlineBeatmapID == target.OnlineBeatmapID); + AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID); + + AddStep("reset filter text", () => songSelect.FilterControl.ChildrenOfType().First().Text = string.Empty); + + AddAssert("game still correct", () => Beatmap.Value?.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID); + AddAssert("carousel still correct", () => songSelect.Carousel.SelectedBeatmap.OnlineBeatmapID == target.OnlineBeatmapID); + } + [Test] public void TestAutoplayViaCtrlEnter() { @@ -468,6 +507,7 @@ public void TestHideSetSelectsCorrectBeatmap() private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait(); private static int importId; + private int getImportId() => ++importId; private void checkMusicPlaying(bool playing) => @@ -551,6 +591,8 @@ private class TestSongSelect : PlaySongSelect public new Bindable Ruleset => base.Ruleset; + public new FilterControl FilterControl => base.FilterControl; + public WorkingBeatmap CurrentBeatmap => Beatmap.Value; public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public new BeatmapCarousel Carousel => base.Carousel; diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index 351e5df17a..f6e03d40ff 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -13,6 +13,7 @@ namespace osu.Game.Beatmaps.Drawables public class BeatmapSetOnlineStatusPill : CircularContainer { private readonly OsuSpriteText statusText; + private readonly Box background; private BeatmapSetOnlineStatus status; @@ -43,6 +44,12 @@ public MarginPadding TextPadding set => statusText.Padding = value; } + public Color4 BackgroundColour + { + get => background.Colour; + set => background.Colour = value; + } + public BeatmapSetOnlineStatusPill() { AutoSizeAxes = Axes.Both; @@ -50,7 +57,7 @@ public BeatmapSetOnlineStatusPill() Children = new Drawable[] { - new Box + background = new Box { RelativeSizeAxes = Axes.Both, Colour = Color4.Black, diff --git a/osu.Game/Configuration/SettingSourceAttribute.cs b/osu.Game/Configuration/SettingSourceAttribute.cs index a3788e4582..4bdbb5fc24 100644 --- a/osu.Game/Configuration/SettingSourceAttribute.cs +++ b/osu.Game/Configuration/SettingSourceAttribute.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using JetBrains.Annotations; using osu.Framework.Bindables; @@ -16,6 +17,10 @@ namespace osu.Game.Configuration /// An attribute to mark a bindable as being exposed to the user via settings controls. /// Can be used in conjunction with to automatically create UI controls. /// + /// + /// All controls with set will be placed first in ascending order. + /// All controls with no will come afterward in default order. + /// [MeansImplicitUse] [AttributeUsage(AttributeTargets.Property)] public class SettingSourceAttribute : Attribute @@ -24,18 +29,26 @@ public class SettingSourceAttribute : Attribute public string Description { get; } + public int? OrderPosition { get; } + public SettingSourceAttribute(string label, string description = null) { Label = label ?? string.Empty; Description = description ?? string.Empty; } + + public SettingSourceAttribute(string label, string description, int orderPosition) + : this(label, description) + { + OrderPosition = orderPosition; + } } public static class SettingSourceExtensions { public static IEnumerable CreateSettingsControls(this object obj) { - foreach (var (attr, property) in obj.GetSettingsSourceProperties()) + foreach (var (attr, property) in obj.GetOrderedSettingsSourceProperties()) { object value = property.GetValue(obj); @@ -116,5 +129,15 @@ public static IEnumerable CreateSettingsControls(this object obj) yield return (attr, property); } } + + public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetOrderedSettingsSourceProperties(this object obj) + { + var original = obj.GetSettingsSourceProperties(); + + var orderedRelative = original.Where(attr => attr.Item1.OrderPosition != null).OrderBy(attr => attr.Item1.OrderPosition); + var unordered = original.Except(orderedRelative); + + return orderedRelative.Concat(unordered); + } } } diff --git a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs index baca57ea89..1d67c4e033 100644 --- a/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs +++ b/osu.Game/Graphics/UserInterface/GradientLineTabControl.cs @@ -49,14 +49,7 @@ private class GradientLine : GridContainer public GradientLine() { RelativeSizeAxes = Axes.X; - Size = new Vector2(0.8f, 1.5f); - - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(mode: GridSizeMode.Relative, size: 0.4f), - new Dimension(), - }; + Size = new Vector2(0.8f, 1f); Content = new[] { @@ -65,16 +58,12 @@ public GradientLine() new Box { RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Color4.White) + Colour = ColourInfo.GradientHorizontal(Color4.Transparent, Colour) }, new Box { RelativeSizeAxes = Axes.Both, - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.Transparent) + Colour = ColourInfo.GradientHorizontal(Colour, Color4.Transparent) }, } }; diff --git a/osu.Game/Online/API/Requests/Responses/APISpotlight.cs b/osu.Game/Online/API/Requests/Responses/APISpotlight.cs index 3a002e57b2..4f63ebe3df 100644 --- a/osu.Game/Online/API/Requests/Responses/APISpotlight.cs +++ b/osu.Game/Online/API/Requests/Responses/APISpotlight.cs @@ -26,6 +26,9 @@ public class APISpotlight [JsonProperty(@"end_date")] public DateTimeOffset EndDate; + [JsonProperty(@"participant_count")] + public int? Participants; + public override string ToString() => Name; } } diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index e0360c6312..53003b0488 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -150,7 +150,7 @@ private void load(IAPIProvider api, BeatmapManager beatmaps) }, new OsuSpriteText { - Text = BeatmapSet.Value.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty, + Text = getVideoSuffixText(), Font = OsuFont.GetFont(size: 11, weight: FontWeight.Bold) }, }; @@ -163,5 +163,13 @@ private void load(IAPIProvider api, BeatmapManager beatmaps) private void userChanged(ValueChangedEvent e) => button.Enabled.Value = !(e.NewValue is GuestUser); private void enabledChanged(ValueChangedEvent e) => this.FadeColour(e.NewValue ? Color4.White : Color4.Gray, 200, Easing.OutQuint); + + private string getVideoSuffixText() + { + if (!BeatmapSet.Value.OnlineInfo.HasVideo) + return string.Empty; + + return noVideo ? "without Video" : "with Video"; + } } } diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 8c884e0950..7eae05e4a9 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -3,7 +3,6 @@ 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; @@ -14,7 +13,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Overlays.Direct; using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet.Buttons { @@ -22,7 +20,7 @@ public class PreviewButton : OsuClickableContainer { private const float transition_duration = 500; - private readonly Box bg, progress; + private readonly Box background, progress; private readonly PlayButton playButton; private PreviewTrack preview => playButton.Preview; @@ -40,10 +38,10 @@ public PreviewButton() Children = new Drawable[] { - bg = new Box + background = new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.25f), + Alpha = 0.5f }, new Container { @@ -71,9 +69,10 @@ public PreviewButton() } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, OverlayColourProvider colourProvider) { progress.Colour = colours.Yellow; + background.Colour = colourProvider.Background6; } protected override void Update() @@ -91,13 +90,13 @@ protected override void Update() protected override bool OnHover(HoverEvent e) { - bg.FadeColour(Color4.Black.Opacity(0.5f), 100); + background.FadeTo(0.75f, 80); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - bg.FadeColour(Color4.Black.Opacity(0.25f), 100); + background.FadeTo(0.5f, 80); base.OnHoverLost(e); } } diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index d76f6a43db..488e181fa2 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -10,7 +9,6 @@ using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Screens.Select.Details; using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet { @@ -21,6 +19,7 @@ public class Details : FillFlowContainer private readonly PreviewButton preview; private readonly BasicStats basic; private readonly AdvancedStats advanced; + private readonly DetailBox ratingBox; private BeatmapSetInfo beatmapSet; @@ -54,6 +53,7 @@ public BeatmapInfo Beatmap private void updateDisplay() { Ratings.Metrics = BeatmapSet?.Metrics; + ratingBox.Alpha = BeatmapSet?.OnlineInfo?.Status > 0 ? 1 : 0; } public Details() @@ -86,7 +86,7 @@ public Details() Margin = new MarginPadding { Vertical = 7.5f }, }, }, - new DetailBox + ratingBox = new DetailBox { Child = Ratings = new UserRatings { @@ -107,6 +107,8 @@ private void load() private class DetailBox : Container { private readonly Container content; + private readonly Box background; + protected override Container Content => content; public DetailBox() @@ -116,10 +118,10 @@ public DetailBox() InternalChildren = new Drawable[] { - new Box + background = new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f), + Alpha = 0.5f }, content = new Container { @@ -129,6 +131,12 @@ public DetailBox() }, }; } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + background.Colour = colourProvider.Background6; + } } } } diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index 7c5c5a9d55..29f09a1ad8 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// 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; @@ -30,6 +30,7 @@ public class Header : BeatmapDownloadTrackingComposite private const float buttons_spacing = 5; private readonly UpdateableBeatmapSetCover cover; + private readonly Box coverGradient; private readonly OsuSpriteText title, artist; private readonly AuthorInfo author; private readonly FillFlowContainer downloadButtonsContainer; @@ -93,10 +94,9 @@ public Header() RelativeSizeAxes = Axes.Both, Masking = true, }, - new Box + coverGradient = new Box { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.3f), Color4.Black.Opacity(0.8f)), + RelativeSizeAxes = Axes.Both }, }, }, @@ -106,8 +106,7 @@ public Header() AutoSizeAxes = Axes.Y, Padding = new MarginPadding { - Top = 20, - Bottom = 30, + Vertical = BeatmapSetOverlay.Y_PADDING, Left = BeatmapSetOverlay.X_PADDING, Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH, }, @@ -130,11 +129,12 @@ public Header() { Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 15 }, Children = new Drawable[] { title = new OsuSpriteText { - Font = OsuFont.GetFont(size: 37, weight: FontWeight.Bold, italics: true) + Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true) }, externalLink = new ExternalLinkButton { @@ -144,7 +144,7 @@ public Header() }, } }, - artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 25, weight: FontWeight.SemiBold, italics: true) }, + artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true) }, new Container { RelativeSizeAxes = Axes.X, @@ -187,7 +187,7 @@ public Header() Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, AutoSizeAxes = Axes.Both, - Margin = new MarginPadding { Top = BeatmapSetOverlay.TOP_PADDING, Right = BeatmapSetOverlay.X_PADDING }, + Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = BeatmapSetOverlay.X_PADDING }, Direction = FillDirection.Vertical, Spacing = new Vector2(10), Children = new Drawable[] @@ -197,7 +197,7 @@ public Header() Anchor = Anchor.TopRight, Origin = Anchor.TopRight, TextSize = 14, - TextPadding = new MarginPadding { Horizontal = 25, Vertical = 8 } + TextPadding = new MarginPadding { Horizontal = 35, Vertical = 10 } }, Details = new Details(), }, @@ -215,8 +215,11 @@ public Header() } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colourProvider) { + coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f)); + onlineStatusPill.BackgroundColour = colourProvider.Background6; + State.BindValueChanged(_ => updateDownloadButtons()); BeatmapSet.BindValueChanged(setInfo => diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index d7392b31e1..85e871baca 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -38,6 +38,8 @@ public BeatmapInfo Beatmap public Info() { MetadataSection source, tags, genre, language; + OsuSpriteText unrankedPlaceholder; + RelativeSizeAxes = Axes.X; Height = 220; Masking = true; @@ -110,6 +112,14 @@ public Info() RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = 20, Horizontal = 15 }, }, + unrankedPlaceholder = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0, + Text = "Unranked beatmap", + Font = OsuFont.GetFont(size: 12) + }, }, }, }, @@ -122,6 +132,9 @@ public Info() tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty; genre.Text = b.NewValue?.OnlineInfo?.Genre?.Name ?? string.Empty; language.Text = b.NewValue?.OnlineInfo?.Language?.Name ?? string.Empty; + var setHasLeaderboard = b.NewValue?.OnlineInfo?.Status > 0; + successRate.Alpha = setHasLeaderboard ? 1 : 0; + unrankedPlaceholder.Alpha = setHasLeaderboard ? 0 : 1; }; } diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs index e2a725ec46..20a3b09db4 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardScopeSelector.cs @@ -26,10 +26,10 @@ public LeaderboardScopeSelector() } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colourProvider) { - AccentColour = colours.Blue; - LineColour = Color4.Gray; + AccentColour = colourProvider.Highlight1; + LineColour = colourProvider.Background1; } private class ScopeSelectorTabItem : PageTabItem diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 758140a12d..43a45bd2fc 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -77,9 +77,9 @@ private TableColumn[] createHeaders(ScoreInfo score) new TableColumn("rank", Anchor.CentreRight, new Dimension(GridSizeMode.AutoSize)), new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 70)), // grade new TableColumn("score", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("accuracy", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("accuracy", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 60, maxSize: 70)), new TableColumn("player", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 150)), - new TableColumn("max combo", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 90)) + new TableColumn("max combo", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 110)) }; foreach (var statistic in score.SortedStatistics) diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs index 14ea3e6b38..83271efe09 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs @@ -30,7 +30,7 @@ public ScoreTableRowBackground(int index, ScoreInfo score) RelativeSizeAxes = Axes.X; Height = 25; - CornerRadius = 3; + CornerRadius = 5; Masking = true; InternalChildren = new Drawable[] diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 0a3b5d9457..8560232209 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -164,7 +164,7 @@ public ScoresContainer() { RelativeSizeAxes = Axes.Both, Masking = true, - CornerRadius = 10, + CornerRadius = 5, Child = loading = new DimmedLoadingLayer(iconScale: 0.8f) { Alpha = 0, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 72a7efd777..8a368aa535 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -96,13 +96,14 @@ public TopScoreUserSection() { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold) + Font = OsuFont.GetFont(size: 10) }, flag = new UpdateableFlag { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, Size = new Vector2(19, 13), + Margin = new MarginPadding { Top = 3 }, // makes spacing look more even ShowPlaceholderOnNull = false, }, } diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index 1dcc847760..15216b6e69 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -42,7 +42,7 @@ private void updateDisplay() int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0; var rate = playCount != 0 ? (float)passCount / playCount : 0; - successPercent.Text = rate.ToString("0%"); + successPercent.Text = rate.ToString("0.#%"); successRate.Length = rate; percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); @@ -105,10 +105,10 @@ public SuccessRate() } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, OverlayColourProvider colourProvider) { successRate.AccentColour = colours.Green; - successRate.BackgroundColour = colours.GrayD; + successRate.BackgroundColour = colourProvider.Background6; updateDisplay(); } diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index f747cfff16..7624351e41 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays public class BeatmapSetOverlay : FullscreenOverlay { public const float X_PADDING = 40; - public const float TOP_PADDING = 25; + public const float Y_PADDING = 25; public const float RIGHT_WIDTH = 275; protected readonly Header Header; diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 94afe4e5a5..2674b3a81e 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -6,25 +6,14 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Users; -using System.Collections.Generic; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Allocation; namespace osu.Game.Overlays.Rankings { public class RankingsOverlayHeader : TabControlOverlayHeader { public readonly Bindable Ruleset = new Bindable(); - public readonly Bindable Spotlight = new Bindable(); public readonly Bindable Country = new Bindable(); - public IEnumerable Spotlights - { - get => spotlightsContainer.Spotlights; - set => spotlightsContainer.Spotlights = value; - } - protected override ScreenTitle CreateTitle() => new RankingsTitle { Scope = { BindTarget = Current } @@ -35,35 +24,11 @@ public IEnumerable Spotlights Current = Ruleset }; - private SpotlightsContainer spotlightsContainer; - - protected override Drawable CreateContent() => new FillFlowContainer + protected override Drawable CreateContent() => new CountryFilter { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new CountryFilter - { - Current = Country - }, - spotlightsContainer = new SpotlightsContainer - { - Spotlight = { BindTarget = Spotlight } - } - } + Current = Country }; - protected override void LoadComplete() - { - Current.BindValueChanged(onCurrentChanged, true); - base.LoadComplete(); - } - - private void onCurrentChanged(ValueChangedEvent scope) => - spotlightsContainer.FadeTo(scope.NewValue == RankingsScope.Spotlights ? 1 : 0, 200, Easing.OutQuint); - private class RankingsTitle : ScreenTitle { public readonly Bindable Scope = new Bindable(); @@ -81,48 +46,6 @@ protected override void LoadComplete() protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/rankings"); } - - private class SpotlightsContainer : CompositeDrawable - { - public readonly Bindable Spotlight = new Bindable(); - - public IEnumerable Spotlights - { - get => dropdown.Items; - set => dropdown.Items = value; - } - - private readonly OsuDropdown dropdown; - private readonly Box background; - - public SpotlightsContainer() - { - Height = 100; - RelativeSizeAxes = Axes.X; - InternalChildren = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - }, - dropdown = new OsuDropdown - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - Width = 0.8f, - Current = Spotlight, - Y = 20, - } - }; - } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - background.Colour = colourProvider.Dark3; - } - } } public enum RankingsScope diff --git a/osu.Game/Overlays/Rankings/Spotlight.cs b/osu.Game/Overlays/Rankings/Spotlight.cs deleted file mode 100644 index e956b4f449..0000000000 --- a/osu.Game/Overlays/Rankings/Spotlight.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using Newtonsoft.Json; - -namespace osu.Game.Overlays.Rankings -{ - public class Spotlight - { - [JsonProperty("id")] - public int Id; - - [JsonProperty("text")] - public string Text; - - public override string ToString() => Text; - } -} diff --git a/osu.Game/Overlays/Rankings/SpotlightSelector.cs b/osu.Game/Overlays/Rankings/SpotlightSelector.cs index e34c01113e..f019b50ae8 100644 --- a/osu.Game/Overlays/Rankings/SpotlightSelector.cs +++ b/osu.Game/Overlays/Rankings/SpotlightSelector.cs @@ -14,11 +14,14 @@ using System; using System.Collections.Generic; using osu.Framework.Graphics.UserInterface; +using osu.Game.Online.API.Requests; namespace osu.Game.Overlays.Rankings { - public class SpotlightSelector : CompositeDrawable, IHasCurrentValue + public class SpotlightSelector : VisibilityContainer, IHasCurrentValue { + private const int duration = 300; + private readonly Box background; private readonly SpotlightsDropdown dropdown; @@ -36,50 +39,60 @@ public IEnumerable Spotlights set => dropdown.Items = value; } + protected override bool StartHidden => true; + private readonly InfoColumn startDateColumn; private readonly InfoColumn endDateColumn; + private readonly InfoColumn mapCountColumn; + private readonly InfoColumn participantsColumn; + private readonly Container content; public SpotlightSelector() { RelativeSizeAxes = Axes.X; Height = 100; - - InternalChildren = new Drawable[] + Add(content = new Container { - background = new Box + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, - Children = new Drawable[] + background = new Box { - dropdown = new SpotlightsDropdown + RelativeSizeAxes = Axes.Both, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, + Children = new Drawable[] { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - Current = Current, - Depth = -float.MaxValue - }, - new FillFlowContainer - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(15, 0), - Children = new Drawable[] + dropdown = new SpotlightsDropdown { - startDateColumn = new InfoColumn(@"Start Date"), - endDateColumn = new InfoColumn(@"End Date"), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + Current = Current, + Depth = -float.MaxValue + }, + new FillFlowContainer + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(15, 0), + Children = new Drawable[] + { + startDateColumn = new InfoColumn(@"Start Date"), + endDateColumn = new InfoColumn(@"End Date"), + mapCountColumn = new InfoColumn(@"Map Count"), + participantsColumn = new InfoColumn(@"Participants") + } } } } - }, - }; + } + }); } [BackgroundDependencyLoader] @@ -88,18 +101,17 @@ private void load(OverlayColourProvider colourProvider) background.Colour = colourProvider.Dark3; } - protected override void LoadComplete() + public void ShowInfo(GetSpotlightRankingsResponse response) { - base.LoadComplete(); - - Current.BindValueChanged(onCurrentChanged); + startDateColumn.Value = dateToString(response.Spotlight.StartDate); + endDateColumn.Value = dateToString(response.Spotlight.EndDate); + mapCountColumn.Value = response.BeatmapSets.Count.ToString(); + participantsColumn.Value = response.Spotlight.Participants?.ToString("N0"); } - private void onCurrentChanged(ValueChangedEvent spotlight) - { - startDateColumn.Value = dateToString(spotlight.NewValue.StartDate); - endDateColumn.Value = dateToString(spotlight.NewValue.EndDate); - } + protected override void PopIn() => content.FadeIn(duration, Easing.OutQuint); + + protected override void PopOut() => content.FadeOut(duration, Easing.OutQuint); private string dateToString(DateTimeOffset date) => date.ToString("yyyy-MM-dd"); diff --git a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs new file mode 100644 index 0000000000..33811cc982 --- /dev/null +++ b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs @@ -0,0 +1,161 @@ +// 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.Bindables; +using osu.Game.Rulesets; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.API.Requests.Responses; +using osuTK; +using osu.Framework.Allocation; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.Rankings.Tables; +using System.Linq; +using osu.Game.Overlays.Direct; +using System.Threading; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Rankings +{ + public class SpotlightsLayout : CompositeDrawable + { + public readonly Bindable Ruleset = new Bindable(); + + private readonly Bindable selectedSpotlight = new Bindable(); + + [Resolved] + private IAPIProvider api { get; set; } + + [Resolved] + private RulesetStore rulesets { get; set; } + + private CancellationTokenSource cancellationToken; + private GetSpotlightRankingsRequest getRankingsRequest; + private GetSpotlightsRequest spotlightsRequest; + + private SpotlightSelector selector; + private Container content; + private DimmedLoadingLayer loading; + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + InternalChild = new ReverseChildIDFillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + selector = new SpotlightSelector + { + Current = selectedSpotlight, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + content = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Vertical = 10 } + }, + loading = new DimmedLoadingLayer() + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selector.Show(); + + selectedSpotlight.BindValueChanged(onSpotlightChanged); + Ruleset.BindValueChanged(onRulesetChanged); + + getSpotlights(); + } + + private void getSpotlights() + { + spotlightsRequest = new GetSpotlightsRequest(); + spotlightsRequest.Success += response => selector.Spotlights = response.Spotlights; + api.Queue(spotlightsRequest); + } + + private void onRulesetChanged(ValueChangedEvent ruleset) + { + if (!selector.Spotlights.Any()) + return; + + selectedSpotlight.TriggerChange(); + } + + private void onSpotlightChanged(ValueChangedEvent spotlight) + { + loading.Show(); + + cancellationToken?.Cancel(); + getRankingsRequest?.Cancel(); + + getRankingsRequest = new GetSpotlightRankingsRequest(Ruleset.Value, spotlight.NewValue.Id); + getRankingsRequest.Success += onSuccess; + api.Queue(getRankingsRequest); + } + + private void onSuccess(GetSpotlightRankingsResponse response) + { + LoadComponentAsync(createContent(response), loaded => + { + selector.ShowInfo(response); + + content.Clear(); + content.Add(loaded); + + loading.Hide(); + }, (cancellationToken = new CancellationTokenSource()).Token); + } + + private Drawable createContent(GetSpotlightRankingsResponse response) => new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 20), + Children = new Drawable[] + { + new ScoresTable(1, response.Users), + new FillFlowContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Spacing = new Vector2(10), + Children = response.BeatmapSets.Select(b => new DirectGridPanel(b.ToBeatmapSet(rulesets)) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }).ToList() + } + } + }; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + spotlightsRequest?.Cancel(); + getRankingsRequest?.Cancel(); + cancellationToken?.Cancel(); + } + } +} diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs index 84470d9caa..f3215d07fa 100644 --- a/osu.Game/Overlays/RankingsOverlay.cs +++ b/osu.Game/Overlays/RankingsOverlay.cs @@ -24,7 +24,7 @@ public class RankingsOverlay : FullscreenOverlay private readonly Bindable ruleset = new Bindable(); private readonly BasicScrollContainer scrollFlow; - private readonly Container tableContainer; + private readonly Container contentContainer; private readonly DimmedLoadingLayer loading; private readonly Box background; @@ -69,13 +69,13 @@ public RankingsOverlay() AutoSizeAxes = Axes.Y, Children = new Drawable[] { - tableContainer = new Container + contentContainer = new Container { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, - Margin = new MarginPadding { Vertical = 10 } + Margin = new MarginPadding { Bottom = 10 } }, loading = new DimmedLoadingLayer(), } @@ -112,7 +112,13 @@ protected override void LoadComplete() Scheduler.AddOnce(loadNewContent); }, true); - ruleset.BindValueChanged(_ => Scheduler.AddOnce(loadNewContent), true); + ruleset.BindValueChanged(_ => + { + if (Scope.Value == RankingsScope.Spotlights) + return; + + Scheduler.AddOnce(loadNewContent); + }, true); base.LoadComplete(); } @@ -134,17 +140,26 @@ private void loadNewContent() cancellationToken?.Cancel(); lastRequest?.Cancel(); + if (Scope.Value == RankingsScope.Spotlights) + { + loadContent(new SpotlightsLayout + { + Ruleset = { BindTarget = ruleset } + }); + return; + } + var request = createScopedRequest(); lastRequest = request; if (request == null) { - loadTable(null); + loadContent(null); return; } - request.Success += () => loadTable(createTableFromResponse(request)); - request.Failure += _ => loadTable(null); + request.Success += () => loadContent(createTableFromResponse(request)); + request.Failure += _ => loadContent(null); api.Queue(request); } @@ -189,21 +204,21 @@ private Drawable createTableFromResponse(APIRequest request) return null; } - private void loadTable(Drawable table) + private void loadContent(Drawable content) { scrollFlow.ScrollToStart(); - if (table == null) + if (content == null) { - tableContainer.Clear(); + contentContainer.Clear(); loading.Hide(); return; } - LoadComponentAsync(table, t => + LoadComponentAsync(content, loaded => { loading.Hide(); - tableContainer.Child = table; + contentContainer.Child = loaded; }, (cancellationToken = new CancellationTokenSource()).Token); } } diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index d74e2ce2bc..2083671072 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -28,7 +28,11 @@ public abstract class ModDifficultyAdjust : Mod, IApplicableToDifficulty public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModHardRock) }; - [SettingSource("Drain Rate", "Override a beatmap's set HP.")] + protected const int FIRST_SETTING_ORDER = 1; + + protected const int LAST_SETTING_ORDER = 2; + + [SettingSource("HP Drain", "Override a beatmap's set HP.", FIRST_SETTING_ORDER)] public BindableNumber DrainRate { get; } = new BindableFloat { Precision = 0.1f, @@ -38,7 +42,7 @@ public abstract class ModDifficultyAdjust : Mod, IApplicableToDifficulty Value = 5, }; - [SettingSource("Overall Difficulty", "Override a beatmap's set OD.")] + [SettingSource("Accuracy", "Override a beatmap's set OD.", LAST_SETTING_ORDER)] public BindableNumber OverallDifficulty { get; } = new BindableFloat { Precision = 0.1f, diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 592e26adc2..7f36a23a86 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -229,6 +229,17 @@ public bool SelectBeatmap(BeatmapInfo beatmap, bool bypassFilters = true) if (item != null) { select(item); + + // if we got here and the set is filtered, it means we were bypassing filters. + // in this case, reapplying the filter is necessary to ensure the panel is in the correct place + // (since it is forcefully being included in the carousel). + if (set.Filtered.Value) + { + Debug.Assert(bypassFilters); + + applyActiveCriteria(false, true); + } + return true; } } diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 301d0d4dae..8e323c66e2 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -62,7 +62,7 @@ public override int CompareTo(FilterCriteria criteria, CarouselItem other) /// /// All beatmaps which are not filtered and valid for display. /// - protected IEnumerable ValidBeatmaps => Beatmaps.Where(b => !b.Filtered.Value).Select(b => b.Beatmap); + protected IEnumerable ValidBeatmaps => Beatmaps.Where(b => !b.Filtered.Value || b.State.Value == CarouselItemState.Selected).Select(b => b.Beatmap); private int compareUsingAggregateMax(CarouselBeatmapSet other, Func func) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselItem.cs b/osu.Game/Screens/Select/Carousel/CarouselItem.cs index 79c1a4cb6b..1108b72bd2 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselItem.cs @@ -16,7 +16,7 @@ public abstract class CarouselItem /// /// This item is not in a hidden state. /// - public bool Visible => State.Value != CarouselItemState.Collapsed && !Filtered.Value; + public bool Visible => State.Value == CarouselItemState.Selected || (State.Value != CarouselItemState.Collapsed && !Filtered.Value); public virtual List Drawables { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 5037081b5e..0da260d752 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -376,16 +376,22 @@ public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartActio private void workingBeatmapChanged(ValueChangedEvent e) { - if (e.NewValue is DummyWorkingBeatmap) return; + if (e.NewValue is DummyWorkingBeatmap || !this.IsCurrentScreen()) return; - if (this.IsCurrentScreen() && !Carousel.SelectBeatmap(e.NewValue?.BeatmapInfo, false)) + if (!Carousel.SelectBeatmap(e.NewValue.BeatmapInfo, false)) { - // If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch - if (e.NewValue?.BeatmapInfo?.Ruleset != null && !e.NewValue.BeatmapInfo.Ruleset.Equals(decoupledRuleset.Value)) + // A selection may not have been possible with filters applied. + + // There was possibly a ruleset mismatch. This is a case we can help things along by updating the game-wide ruleset to match. + if (e.NewValue.BeatmapInfo.Ruleset != null && !e.NewValue.BeatmapInfo.Ruleset.Equals(decoupledRuleset.Value)) { Ruleset.Value = e.NewValue.BeatmapInfo.Ruleset; - Carousel.SelectBeatmap(e.NewValue.BeatmapInfo); + transferRulesetValue(); } + + // Even if a ruleset mismatch was not the cause (ie. a text filter is applied), + // we still want to forcefully show the new beatmap, bypassing filters. + Carousel.SelectBeatmap(e.NewValue.BeatmapInfo); } }