From 7560d3de0435408c62d7e8ad9fbedfcb1e211747 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 22 Nov 2021 10:52:04 +1100 Subject: [PATCH 01/61] Remove decay factor in Flashlight skill --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 466f0556ab..68434fd3d0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { } - private double skillMultiplier => 0.15; + private double skillMultiplier => 0.07; private double strainDecayBase => 0.15; protected override double DecayWeight => 1.0; protected override int HistoryLength => 10; // Look back for 10 notes is added for the sake of flashlight calculations. @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills // We also want to nerf stacks so that only the first object of the stack is accounted for. double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); - result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; + result += stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } } From 43546992586fcefc8f072deb091004edcf418175 Mon Sep 17 00:00:00 2001 From: MBmasher Date: Tue, 30 Nov 2021 12:51:23 +1100 Subject: [PATCH 02/61] Fix cumulative strain time calculation in Flashlight skill --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 466f0556ab..9c539d5e4b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -45,11 +45,17 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills var osuPrevious = (OsuDifficultyHitObject)Previous[i]; var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); + OsuDifficultyHitObject osuLastPrevious; + if (i == 0) + osuLastPrevious = osuCurrent; + else + osuLastPrevious = (OsuDifficultyHitObject)Previous[i - 1]; + if (!(osuPrevious.BaseObject is Spinner)) { double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; - cumulativeStrainTime += osuPrevious.StrainTime; + cumulativeStrainTime += osuLastPrevious.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. if (i == 0) From a32492cdd5614f9d88c4d3e04b5222a10002b0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 27 Nov 2021 15:27:41 +0100 Subject: [PATCH 03/61] Duplicate `BeatmapCard{-> Extra}` as blueprint for extra card size --- .../Visual/Beatmaps/TestSceneBeatmapCard.cs | 14 + .../Drawables/Cards/BeatmapCardExtra.cs | 430 ++++++++++++++++++ 2 files changed, 444 insertions(+) create mode 100644 osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs index f835d21603..04aea4ac9c 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs @@ -262,5 +262,19 @@ namespace osu.Game.Tests.Visual.Beatmaps }); AddToggleStep("disable/enable expansion", disabled => this.ChildrenOfType().ForEach(card => card.Expanded.Disabled = disabled)); } + + [Test] + public void TestExtra() + { + createTestCase(beatmapSetInfo => new BeatmapCardExtra(beatmapSetInfo)); + + AddToggleStep("toggle expanded state", expanded => + { + var card = this.ChildrenOfType().Last(); + if (!card.Expanded.Disabled) + card.Expanded.Value = expanded; + }); + AddToggleStep("disable/enable expansion", disabled => this.ChildrenOfType().ForEach(card => card.Expanded.Disabled = disabled)); + } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs new file mode 100644 index 0000000000..f244e912a1 --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -0,0 +1,430 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Game.Beatmaps.Drawables.Cards.Buttons; +using osu.Game.Beatmaps.Drawables.Cards.Statistics; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.BeatmapSet; +using osuTK; +using osu.Game.Resources.Localisation.Web; +using DownloadButton = osu.Game.Beatmaps.Drawables.Cards.Buttons.DownloadButton; + +namespace osu.Game.Beatmaps.Drawables.Cards +{ + public class BeatmapCardExtra : OsuClickableContainer + { + private const float width = 408; + private const float height = 100; + private const float icon_area_width = 30; + + public Bindable Expanded { get; } = new BindableBool(); + + private readonly APIBeatmapSet beatmapSet; + private readonly Bindable favouriteState; + + private readonly BeatmapDownloadTracker downloadTracker; + + private BeatmapCardContent content = null!; + + private BeatmapCardThumbnail thumbnail = null!; + + private Container rightAreaBackground = null!; + private Container rightAreaButtons = null!; + + private Container mainContent = null!; + private BeatmapCardContentBackground mainContentBackground = null!; + private FillFlowContainer statisticsContainer = null!; + + private FillFlowContainer idleBottomContent = null!; + private BeatmapCardDownloadProgressBar downloadProgressBar = null!; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + public BeatmapCardExtra(APIBeatmapSet beatmapSet) + : base(HoverSampleSet.Submit) + { + this.beatmapSet = beatmapSet; + favouriteState = new Bindable(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount)); + downloadTracker = new BeatmapDownloadTracker(beatmapSet); + } + + [BackgroundDependencyLoader(true)] + private void load(BeatmapSetOverlay? beatmapSetOverlay) + { + Width = width; + Height = height; + + FillFlowContainer leftIconArea; + GridContainer titleContainer; + GridContainer artistContainer; + + InternalChild = content = new BeatmapCardContent(height) + { + MainContent = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + downloadTracker, + rightAreaBackground = new Container + { + RelativeSizeAxes = Axes.Y, + Width = icon_area_width + 2 * BeatmapCard.CORNER_RADIUS, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + // workaround for masking artifacts at the top & bottom of card, + // which become especially visible on downloaded beatmaps (when the icon area has a lime background). + Padding = new MarginPadding { Vertical = 1 }, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.White + }, + }, + thumbnail = new BeatmapCardThumbnail(beatmapSet) + { + Name = @"Left (icon) area", + Size = new Vector2(height), + Padding = new MarginPadding { Right = BeatmapCard.CORNER_RADIUS }, + Child = leftIconArea = new FillFlowContainer + { + Margin = new MarginPadding(5), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(1) + } + }, + new Container + { + Name = @"Right (button) area", + Width = 30, + RelativeSizeAxes = Axes.Y, + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Padding = new MarginPadding { Vertical = 17.5f }, + Child = rightAreaButtons = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new BeatmapCardIconButton[] + { + new FavouriteButton(beatmapSet) + { + Current = favouriteState, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + }, + new DownloadButton(beatmapSet) + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + State = { BindTarget = downloadTracker.State } + }, + new GoToBeatmapButton(beatmapSet) + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + State = { BindTarget = downloadTracker.State } + } + } + } + }, + mainContent = new Container + { + Name = @"Main content", + X = height - BeatmapCard.CORNER_RADIUS, + Height = height, + CornerRadius = BeatmapCard.CORNER_RADIUS, + Masking = true, + Children = new Drawable[] + { + mainContentBackground = new BeatmapCardContentBackground(beatmapSet) + { + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Horizontal = 10, + Vertical = 4 + }, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + titleContainer = new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new[] + { + new OsuSpriteText + { + Text = new RomanisableString(beatmapSet.TitleUnicode, beatmapSet.Title), + Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold), + RelativeSizeAxes = Axes.X, + Truncate = true + }, + Empty() + } + } + }, + artistContainer = new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.AutoSize) + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize) + }, + Content = new[] + { + new[] + { + new OsuSpriteText + { + Text = createArtistText(), + Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold), + RelativeSizeAxes = Axes.X, + Truncate = true + }, + Empty() + }, + } + }, + new LinkFlowContainer(s => + { + s.Shadow = false; + s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold); + }).With(d => + { + d.AutoSizeAxes = Axes.Both; + d.Margin = new MarginPadding { Top = 2 }; + d.AddText("mapped by ", t => t.Colour = colourProvider.Content2); + d.AddUserLink(beatmapSet.Author); + }), + } + }, + new Container + { + Name = @"Bottom content", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Padding = new MarginPadding + { + Horizontal = 10, + Vertical = 4 + }, + Children = new Drawable[] + { + idleBottomContent = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 3), + AlwaysPresent = true, + Children = new Drawable[] + { + statisticsContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10, 0), + Alpha = 0, + AlwaysPresent = true, + ChildrenEnumerable = createStatistics() + }, + new BeatmapCardExtraInfoRow(beatmapSet) + { + Hovered = _ => + { + content.ScheduleShow(); + return false; + }, + Unhovered = _ => + { + // This hide should only trigger if the expanded content has not shown yet. + // ie. if the user has not shown intent to want to see it (quickly moved over the info row area). + if (!Expanded.Value) + content.ScheduleHide(); + } + } + } + }, + downloadProgressBar = new BeatmapCardDownloadProgressBar + { + RelativeSizeAxes = Axes.X, + Height = 6, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + State = { BindTarget = downloadTracker.State }, + Progress = { BindTarget = downloadTracker.Progress } + } + } + } + } + } + } + }, + ExpandedContent = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 10, Vertical = 13 }, + Child = new BeatmapCardDifficultyList(beatmapSet) + }, + Expanded = { BindTarget = Expanded } + }; + + if (beatmapSet.HasVideo) + leftIconArea.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) }); + + if (beatmapSet.HasStoryboard) + leftIconArea.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) }); + + if (beatmapSet.HasExplicitContent) + { + titleContainer.Content[0][1] = new ExplicitContentBeatmapPill + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding { Left = 5 } + }; + } + + if (beatmapSet.TrackId != null) + { + artistContainer.Content[0][1] = new FeaturedArtistBeatmapPill + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding { Left = 5 } + }; + } + + Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmapSet.OnlineID); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + downloadTracker.State.BindValueChanged(_ => updateState()); + Expanded.BindValueChanged(_ => updateState(), true); + FinishTransforms(true); + } + + protected override bool OnHover(HoverEvent e) + { + updateState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + content.ScheduleHide(); + + updateState(); + base.OnHoverLost(e); + } + + private LocalisableString createArtistText() + { + var romanisableArtist = new RomanisableString(beatmapSet.ArtistUnicode, beatmapSet.Artist); + return BeatmapsetsStrings.ShowDetailsByArtist(romanisableArtist); + } + + private IEnumerable createStatistics() + { + if (beatmapSet.HypeStatus != null) + yield return new HypesStatistic(beatmapSet.HypeStatus); + + // web does not show nominations unless hypes are also present. + // see: https://github.com/ppy/osu-web/blob/8ed7d071fd1d3eaa7e43cf0e4ff55ca2fef9c07c/resources/assets/lib/beatmapset-panel.tsx#L443 + if (beatmapSet.HypeStatus != null && beatmapSet.NominationStatus != null) + yield return new NominationsStatistic(beatmapSet.NominationStatus); + + yield return new FavouritesStatistic(beatmapSet) { Current = favouriteState }; + yield return new PlayCountStatistic(beatmapSet); + + var dateStatistic = BeatmapCardDateStatistic.CreateFor(beatmapSet); + if (dateStatistic != null) + yield return dateStatistic; + } + + private void updateState() + { + bool showDetails = IsHovered || Expanded.Value; + + float targetWidth = width - height; + if (showDetails) + targetWidth = targetWidth - icon_area_width + BeatmapCard.CORNER_RADIUS; + + thumbnail.Dimmed.Value = showDetails; + + // Scale value is intentionally chosen to fit in the spacing of listing displays, as to not overlap horizontally with adjacent cards. + // This avoids depth issues where a hovered (scaled) card to the right of another card would be beneath the card to the left. + content.ScaleTo(Expanded.Value ? 1.03f : 1, 500, Easing.OutQuint); + + mainContent.ResizeWidthTo(targetWidth, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + mainContentBackground.Dimmed.Value = showDetails; + + statisticsContainer.FadeTo(showDetails ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + + rightAreaBackground.FadeColour(downloadTracker.State.Value == DownloadState.LocallyAvailable ? colours.Lime0 : colourProvider.Background3, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + rightAreaButtons.FadeTo(showDetails ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + + foreach (var button in rightAreaButtons) + { + button.IdleColour = downloadTracker.State.Value != DownloadState.LocallyAvailable ? colourProvider.Light1 : colourProvider.Background3; + button.HoverColour = downloadTracker.State.Value != DownloadState.LocallyAvailable ? colourProvider.Content1 : colourProvider.Foreground1; + } + + bool showProgress = downloadTracker.State.Value == DownloadState.Downloading || downloadTracker.State.Value == DownloadState.Importing; + + idleBottomContent.FadeTo(showProgress ? 0 : 1, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + downloadProgressBar.FadeTo(showProgress ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + } + } +} From 61e04f75cc7ad4c5966e605525679e7b28f23ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 27 Nov 2021 15:41:09 +0100 Subject: [PATCH 04/61] Resize extra card to design size --- osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index f244e912a1..7e79bded5d 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -30,8 +30,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards { public class BeatmapCardExtra : OsuClickableContainer { - private const float width = 408; - private const float height = 100; + private const float width = 475; + private const float height = 140; private const float icon_area_width = 30; public Bindable Expanded { get; } = new BindableBool(); From 419fee1380ad94e068b417e7ea3fc1a5244c078e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 27 Nov 2021 15:46:01 +0100 Subject: [PATCH 05/61] Move mapper link to bottom content --- .../Drawables/Cards/BeatmapCardExtra.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 7e79bded5d..f42c96be7c 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -229,17 +229,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards }, } }, - new LinkFlowContainer(s => - { - s.Shadow = false; - s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold); - }).With(d => - { - d.AutoSizeAxes = Axes.Both; - d.Margin = new MarginPadding { Top = 2 }; - d.AddText("mapped by ", t => t.Colour = colourProvider.Content2); - d.AddUserLink(beatmapSet.Author); - }), } }, new Container @@ -265,6 +254,17 @@ namespace osu.Game.Beatmaps.Drawables.Cards AlwaysPresent = true, Children = new Drawable[] { + new LinkFlowContainer(s => + { + s.Shadow = false; + s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold); + }).With(d => + { + d.AutoSizeAxes = Axes.Both; + d.Margin = new MarginPadding { Top = 2 }; + d.AddText("mapped by ", t => t.Colour = colourProvider.Content2); + d.AddUserLink(beatmapSet.Author); + }), statisticsContainer = new FillFlowContainer { RelativeSizeAxes = Axes.X, From 3ecfaa532cb270e36e96344b7f88488105e6b15c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 27 Nov 2021 15:47:52 +0100 Subject: [PATCH 06/61] Add source field to extra beatmap card --- osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs | 1 + osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs index 04aea4ac9c..2a308dd0d4 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs @@ -97,6 +97,7 @@ namespace osu.Game.Tests.Visual.Beatmaps var longName = CreateAPIBeatmapSet(Ruleset.Value); longName.Title = longName.TitleUnicode = "this track has an incredibly and implausibly long title"; longName.Artist = longName.ArtistUnicode = "and this artist! who would have thunk it. it's really such a long name."; + longName.Source = "wow. even the source field has an impossibly long string in it. this really takes the cake, doesn't it?"; longName.HasExplicitContent = true; longName.TrackId = 444; diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index f42c96be7c..1926614c8f 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -229,6 +229,15 @@ namespace osu.Game.Beatmaps.Drawables.Cards }, } }, + new OsuSpriteText + { + RelativeSizeAxes = Axes.X, + Truncate = true, + Text = beatmapSet.Source, + Shadow = false, + Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold), + Colour = colourProvider.Content2 + }, } }, new Container From 2d739c95ea133a9b0068a283a7296700a6377f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 27 Nov 2021 16:01:35 +0100 Subject: [PATCH 07/61] Lay out extra card statistics in grid as per design --- .../Drawables/Cards/BeatmapCardExtra.cs | 54 +++++++++++++------ 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 1926614c8f..663bb26919 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -3,7 +3,6 @@ #nullable enable -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -50,7 +49,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards private Container mainContent = null!; private BeatmapCardContentBackground mainContentBackground = null!; - private FillFlowContainer statisticsContainer = null!; + private GridContainer statisticsContainer = null!; private FillFlowContainer idleBottomContent = null!; private BeatmapCardDownloadProgressBar downloadProgressBar = null!; @@ -274,15 +273,26 @@ namespace osu.Game.Beatmaps.Drawables.Cards d.AddText("mapped by ", t => t.Colour = colourProvider.Content2); d.AddUserLink(beatmapSet.Author); }), - statisticsContainer = new FillFlowContainer + statisticsContainer = new GridContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(10, 0), - Alpha = 0, - AlwaysPresent = true, - ChildrenEnumerable = createStatistics() + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize) + }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension() + }, + Content = new[] + { + new Drawable[3], + new Drawable[3] + } }, new BeatmapCardExtraInfoRow(beatmapSet) { @@ -352,6 +362,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards }; } + createStatistics(); + Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmapSet.OnlineID); } @@ -384,22 +396,32 @@ namespace osu.Game.Beatmaps.Drawables.Cards return BeatmapsetsStrings.ShowDetailsByArtist(romanisableArtist); } - private IEnumerable createStatistics() + private void createStatistics() { + BeatmapCardStatistic withMargin(BeatmapCardStatistic original) + { + original.Margin = new MarginPadding { Right = 10 }; + return original; + } + + statisticsContainer.Content[0][0] = withMargin(new FavouritesStatistic(beatmapSet) + { + Current = favouriteState, + }); + + statisticsContainer.Content[1][0] = withMargin(new PlayCountStatistic(beatmapSet)); + if (beatmapSet.HypeStatus != null) - yield return new HypesStatistic(beatmapSet.HypeStatus); + statisticsContainer.Content[0][1] = withMargin(new HypesStatistic(beatmapSet.HypeStatus)); // web does not show nominations unless hypes are also present. // see: https://github.com/ppy/osu-web/blob/8ed7d071fd1d3eaa7e43cf0e4ff55ca2fef9c07c/resources/assets/lib/beatmapset-panel.tsx#L443 if (beatmapSet.HypeStatus != null && beatmapSet.NominationStatus != null) - yield return new NominationsStatistic(beatmapSet.NominationStatus); - - yield return new FavouritesStatistic(beatmapSet) { Current = favouriteState }; - yield return new PlayCountStatistic(beatmapSet); + statisticsContainer.Content[1][1] = withMargin(new NominationsStatistic(beatmapSet.NominationStatus)); var dateStatistic = BeatmapCardDateStatistic.CreateFor(beatmapSet); if (dateStatistic != null) - yield return dateStatistic; + statisticsContainer.Content[0][2] = withMargin(dateStatistic); } private void updateState() @@ -419,8 +441,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards mainContent.ResizeWidthTo(targetWidth, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); mainContentBackground.Dimmed.Value = showDetails; - statisticsContainer.FadeTo(showDetails ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); - rightAreaBackground.FadeColour(downloadTracker.State.Value == DownloadState.LocallyAvailable ? colours.Lime0 : colourProvider.Background3, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); rightAreaButtons.FadeTo(showDetails ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); From 083ee92deea8f018032fcaa9d111f2192dd76a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 27 Nov 2021 16:09:00 +0100 Subject: [PATCH 08/61] Adjust button vertical padding --- osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 663bb26919..f47e1a7eab 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -121,7 +121,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards RelativeSizeAxes = Axes.Y, Origin = Anchor.TopRight, Anchor = Anchor.TopRight, - Padding = new MarginPadding { Vertical = 17.5f }, + Padding = new MarginPadding { Vertical = 35 }, Child = rightAreaButtons = new Container { RelativeSizeAxes = Axes.Both, From 883fcf26044bef614e4d6d08d9e4719acb537ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 17 Dec 2021 11:44:36 +0100 Subject: [PATCH 09/61] Fix tests --- .../Visual/Beatmaps/TestSceneBeatmapCard.cs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs index 55dbf89334..2b98c61e9b 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs @@ -255,6 +255,12 @@ namespace osu.Game.Tests.Visual.Beatmaps createTestCase(beatmapSetInfo => new BeatmapCard(beatmapSetInfo)); } + [Test] + public void TestExtra() + { + createTestCase(beatmapSetInfo => new BeatmapCardExtra(beatmapSetInfo)); + } + [Test] public void TestHoverState() { @@ -280,19 +286,5 @@ namespace osu.Game.Tests.Visual.Beatmaps BeatmapCard firstCard() => this.ChildrenOfType().First(); } - - [Test] - public void TestExtra() - { - createTestCase(beatmapSetInfo => new BeatmapCardExtra(beatmapSetInfo)); - - AddToggleStep("toggle expanded state", expanded => - { - var card = this.ChildrenOfType().Last(); - if (!card.Expanded.Disabled) - card.Expanded.Value = expanded; - }); - AddToggleStep("disable/enable expansion", disabled => this.ChildrenOfType().ForEach(card => card.Expanded.Disabled = disabled)); - } } } From 3fa45479b0cc3c5d0dbd9e024f22cf5b954d29a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 17 Dec 2021 11:13:07 +0100 Subject: [PATCH 10/61] Share hype/nomination statistic show logic --- osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs | 12 ++++++------ .../Beatmaps/Drawables/Cards/BeatmapCardExtra.cs | 12 ++++++------ .../Drawables/Cards/Statistics/HypesStatistic.cs | 7 ++++++- .../Cards/Statistics/NominationsStatistic.cs | 9 ++++++++- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index 1e24501426..14c29f8e99 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -380,13 +380,13 @@ namespace osu.Game.Beatmaps.Drawables.Cards private IEnumerable createStatistics() { - if (beatmapSet.HypeStatus != null) - yield return new HypesStatistic(beatmapSet.HypeStatus); + var hypesStatistic = HypesStatistic.CreateFor(beatmapSet); + if (hypesStatistic != null) + yield return hypesStatistic; - // web does not show nominations unless hypes are also present. - // see: https://github.com/ppy/osu-web/blob/8ed7d071fd1d3eaa7e43cf0e4ff55ca2fef9c07c/resources/assets/lib/beatmapset-panel.tsx#L443 - if (beatmapSet.HypeStatus != null && beatmapSet.NominationStatus != null) - yield return new NominationsStatistic(beatmapSet.NominationStatus); + var nominationsStatistic = NominationsStatistic.CreateFor(beatmapSet); + if (nominationsStatistic != null) + yield return nominationsStatistic; yield return new FavouritesStatistic(beatmapSet) { Current = favouriteState }; yield return new PlayCountStatistic(beatmapSet); diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 8d3c606d76..2ccc732119 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -409,13 +409,13 @@ namespace osu.Game.Beatmaps.Drawables.Cards statisticsContainer.Content[1][0] = withMargin(new PlayCountStatistic(beatmapSet)); - if (beatmapSet.HypeStatus != null) - statisticsContainer.Content[0][1] = withMargin(new HypesStatistic(beatmapSet.HypeStatus)); + var hypesStatistic = HypesStatistic.CreateFor(beatmapSet); + if (hypesStatistic != null) + statisticsContainer.Content[0][1] = withMargin(hypesStatistic); - // web does not show nominations unless hypes are also present. - // see: https://github.com/ppy/osu-web/blob/8ed7d071fd1d3eaa7e43cf0e4ff55ca2fef9c07c/resources/assets/lib/beatmapset-panel.tsx#L443 - if (beatmapSet.HypeStatus != null && beatmapSet.NominationStatus != null) - statisticsContainer.Content[1][1] = withMargin(new NominationsStatistic(beatmapSet.NominationStatus)); + var nominationsStatistic = NominationsStatistic.CreateFor(beatmapSet); + if (nominationsStatistic != null) + statisticsContainer.Content[1][1] = withMargin(nominationsStatistic); var dateStatistic = BeatmapCardDateStatistic.CreateFor(beatmapSet); if (dateStatistic != null) diff --git a/osu.Game/Beatmaps/Drawables/Cards/Statistics/HypesStatistic.cs b/osu.Game/Beatmaps/Drawables/Cards/Statistics/HypesStatistic.cs index 3fe31c7a41..521d1a5f21 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/Statistics/HypesStatistic.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/Statistics/HypesStatistic.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; @@ -12,11 +14,14 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Statistics /// public class HypesStatistic : BeatmapCardStatistic { - public HypesStatistic(BeatmapSetHypeStatus hypeStatus) + private HypesStatistic(BeatmapSetHypeStatus hypeStatus) { Icon = FontAwesome.Solid.Bullhorn; Text = hypeStatus.Current.ToLocalisableString(); TooltipText = BeatmapsStrings.HypeRequiredText(hypeStatus.Current.ToLocalisableString(), hypeStatus.Required.ToLocalisableString()); } + + public static HypesStatistic? CreateFor(IBeatmapSetOnlineInfo beatmapSetOnlineInfo) + => beatmapSetOnlineInfo.HypeStatus == null ? null : new HypesStatistic(beatmapSetOnlineInfo.HypeStatus); } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/Statistics/NominationsStatistic.cs b/osu.Game/Beatmaps/Drawables/Cards/Statistics/NominationsStatistic.cs index f09269a615..23bd6ef0a9 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/Statistics/NominationsStatistic.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/Statistics/NominationsStatistic.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; @@ -12,11 +14,16 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Statistics /// public class NominationsStatistic : BeatmapCardStatistic { - public NominationsStatistic(BeatmapSetNominationStatus nominationStatus) + private NominationsStatistic(BeatmapSetNominationStatus nominationStatus) { Icon = FontAwesome.Solid.ThumbsUp; Text = nominationStatus.Current.ToLocalisableString(); TooltipText = BeatmapsStrings.NominationsRequiredText(nominationStatus.Current.ToLocalisableString(), nominationStatus.Required.ToLocalisableString()); } + + public static NominationsStatistic? CreateFor(IBeatmapSetOnlineInfo beatmapSetOnlineInfo) + // web does not show nominations unless hypes are also present. + // see: https://github.com/ppy/osu-web/blob/8ed7d071fd1d3eaa7e43cf0e4ff55ca2fef9c07c/resources/assets/lib/beatmapset-panel.tsx#L443 + => beatmapSetOnlineInfo.HypeStatus == null || beatmapSetOnlineInfo.NominationStatus == null ? null : new NominationsStatistic(beatmapSetOnlineInfo.NominationStatus); } } From 7aab12d4b061d6621e5c3b5e5bc36be8a75e8c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 17 Dec 2021 11:27:38 +0100 Subject: [PATCH 11/61] Share extra row dropdown show/cancel show logic --- .../Beatmaps/Drawables/Cards/BeatmapCard.cs | 38 +++++++------------ .../Drawables/Cards/BeatmapCardExtra.cs | 38 +++++++------------ .../Cards/BeatmapCardExtraInfoRow.cs | 25 +++++++++++- 3 files changed, 49 insertions(+), 52 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index 14c29f8e99..4892abc846 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -44,7 +44,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards private readonly BeatmapDownloadTracker downloadTracker; - private BeatmapCardContent content = null!; + [Cached] + private readonly BeatmapCardContent content; private BeatmapCardThumbnail thumbnail = null!; @@ -72,6 +73,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards this.beatmapSet = beatmapSet; favouriteState = new Bindable(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount)); downloadTracker = new BeatmapDownloadTracker(beatmapSet); + content = new BeatmapCardContent(height); } [BackgroundDependencyLoader(true)] @@ -80,13 +82,13 @@ namespace osu.Game.Beatmaps.Drawables.Cards Width = width; Height = height; - FillFlowContainer leftIconArea; - GridContainer titleContainer; - GridContainer artistContainer; + FillFlowContainer leftIconArea = null!; + GridContainer titleContainer = null!; + GridContainer artistContainer = null!; - InternalChild = content = new BeatmapCardContent(height) + InternalChild = content.With(c => { - MainContent = new Container + c.MainContent = new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] @@ -281,20 +283,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards ChildrenEnumerable = createStatistics() }, new BeatmapCardExtraInfoRow(beatmapSet) - { - Hovered = _ => - { - content.ExpandAfterDelay(); - return false; - }, - Unhovered = _ => - { - // Handles the case where a user has not shown explicit intent to view expanded info. - // ie. quickly moved over the info row area but didn't remain within it. - if (!Expanded.Value) - content.CancelExpand(); - } - } } }, downloadProgressBar = new BeatmapCardDownloadProgressBar @@ -311,16 +299,16 @@ namespace osu.Game.Beatmaps.Drawables.Cards } } } - }, - ExpandedContent = new Container + }; + c.ExpandedContent = new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = 10, Vertical = 13 }, Child = new BeatmapCardDifficultyList(beatmapSet) - }, - Expanded = { BindTarget = Expanded } - }; + }; + c.Expanded.BindTarget = Expanded; + }); if (beatmapSet.HasVideo) leftIconArea.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) }); diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 2ccc732119..328294a323 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -40,7 +40,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards private readonly BeatmapDownloadTracker downloadTracker; - private BeatmapCardContent content = null!; + [Cached] + private readonly BeatmapCardContent content; private BeatmapCardThumbnail thumbnail = null!; @@ -66,6 +67,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards this.beatmapSet = beatmapSet; favouriteState = new Bindable(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount)); downloadTracker = new BeatmapDownloadTracker(beatmapSet); + content = new BeatmapCardContent(height); } [BackgroundDependencyLoader(true)] @@ -74,13 +76,13 @@ namespace osu.Game.Beatmaps.Drawables.Cards Width = width; Height = height; - FillFlowContainer leftIconArea; - GridContainer titleContainer; - GridContainer artistContainer; + FillFlowContainer leftIconArea = null!; + GridContainer titleContainer = null!; + GridContainer artistContainer = null!; - InternalChild = content = new BeatmapCardContent(height) + InternalChild = content.With(c => { - MainContent = new Container + c.MainContent = new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] @@ -295,20 +297,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards } }, new BeatmapCardExtraInfoRow(beatmapSet) - { - Hovered = _ => - { - content.ExpandAfterDelay(); - return false; - }, - Unhovered = _ => - { - // This hide should only trigger if the expanded content has not shown yet. - // ie. if the user has not shown intent to want to see it (quickly moved over the info row area). - if (!Expanded.Value) - content.CancelExpand(); - } - } } }, downloadProgressBar = new BeatmapCardDownloadProgressBar @@ -325,16 +313,16 @@ namespace osu.Game.Beatmaps.Drawables.Cards } } } - }, - ExpandedContent = new Container + }; + c.ExpandedContent = new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = 10, Vertical = 13 }, Child = new BeatmapCardDifficultyList(beatmapSet) - }, - Expanded = { BindTarget = Expanded } - }; + }; + c.Expanded.BindTarget = Expanded; + }); if (beatmapSet.HasVideo) leftIconArea.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) }); diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs index 0a9d98e621..2d411ad344 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs @@ -1,21 +1,28 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Beatmaps.Drawables.Cards { - public class BeatmapCardExtraInfoRow : HoverHandlingContainer + public class BeatmapCardExtraInfoRow : CompositeDrawable { + [Resolved(CanBeNull = true)] + private BeatmapCardContent? content { get; set; } + public BeatmapCardExtraInfoRow(APIBeatmapSet beatmapSet) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Child = new FillFlowContainer + InternalChild = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -39,5 +46,19 @@ namespace osu.Game.Beatmaps.Drawables.Cards } }; } + + protected override bool OnHover(HoverEvent e) + { + content?.ExpandAfterDelay(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + if (content?.Expanded.Value == false) + content.CancelExpand(); + + base.OnHoverLost(e); + } } } From f052b47d873cbe41277295a6ac75525a72fe996e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 17 Dec 2021 12:58:05 +0100 Subject: [PATCH 12/61] Extract collapsible button container for card usage --- .../Beatmaps/Drawables/Cards/BeatmapCard.cs | 104 +--------- .../Drawables/Cards/BeatmapCardExtra.cs | 104 +--------- .../Cards/CollapsibleButtonContainer.cs | 184 ++++++++++++++++++ 3 files changed, 200 insertions(+), 192 deletions(-) create mode 100644 osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainer.cs diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index 4892abc846..13c4cfe207 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -8,11 +8,9 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; -using osu.Game.Beatmaps.Drawables.Cards.Buttons; using osu.Game.Beatmaps.Drawables.Cards.Statistics; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -24,7 +22,6 @@ using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; using osuTK; using osu.Game.Resources.Localisation.Web; -using DownloadButton = osu.Game.Beatmaps.Drawables.Cards.Buttons.DownloadButton; namespace osu.Game.Beatmaps.Drawables.Cards { @@ -37,7 +34,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards private const float width = 408; private const float height = 100; - private const float icon_area_width = 30; private readonly APIBeatmapSet beatmapSet; private readonly Bindable favouriteState; @@ -48,20 +44,13 @@ namespace osu.Game.Beatmaps.Drawables.Cards private readonly BeatmapCardContent content; private BeatmapCardThumbnail thumbnail = null!; + private CollapsibleButtonContainer buttonContainer = null!; - private Container rightAreaBackground = null!; - private Container rightAreaButtons = null!; - - private Container mainContent = null!; - private BeatmapCardContentBackground mainContentBackground = null!; private FillFlowContainer statisticsContainer = null!; private FillFlowContainer idleBottomContent = null!; private BeatmapCardDownloadProgressBar downloadProgressBar = null!; - [Resolved] - private OsuColour colours { get; set; } = null!; - [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -94,21 +83,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards Children = new Drawable[] { downloadTracker, - rightAreaBackground = new Container - { - RelativeSizeAxes = Axes.Y, - Width = icon_area_width + 2 * CORNER_RADIUS, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - // workaround for masking artifacts at the top & bottom of card, - // which become especially visible on downloaded beatmaps (when the icon area has a lime background). - Padding = new MarginPadding { Vertical = 1 }, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Colour4.White - }, - }, thumbnail = new BeatmapCardThumbnail(beatmapSet) { Name = @"Left (icon) area", @@ -122,61 +96,19 @@ namespace osu.Game.Beatmaps.Drawables.Cards Spacing = new Vector2(1) } }, - new Container + buttonContainer = new CollapsibleButtonContainer(beatmapSet) { - Name = @"Right (button) area", - Width = 30, - RelativeSizeAxes = Axes.Y, - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - Padding = new MarginPadding { Vertical = 17.5f }, - Child = rightAreaButtons = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new BeatmapCardIconButton[] - { - new FavouriteButton(beatmapSet) - { - Current = favouriteState, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre - }, - new DownloadButton(beatmapSet) - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - State = { BindTarget = downloadTracker.State } - }, - new GoToBeatmapButton(beatmapSet) - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - State = { BindTarget = downloadTracker.State } - } - } - } - }, - mainContent = new Container - { - Name = @"Main content", X = height - CORNER_RADIUS, - Height = height, - CornerRadius = CORNER_RADIUS, - Masking = true, + Width = width - height + CORNER_RADIUS, + FavouriteState = { BindTarget = favouriteState }, + ButtonsCollapsedWidth = CORNER_RADIUS, + ButtonsExpandedWidth = 30, + ButtonsPadding = new MarginPadding { Vertical = 17.5f }, Children = new Drawable[] { - mainContentBackground = new BeatmapCardContentBackground(beatmapSet) - { - RelativeSizeAxes = Axes.Both, - }, new FillFlowContainer { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Horizontal = 10, - Vertical = 4 - }, Direction = FillDirection.Vertical, Children = new Drawable[] { @@ -256,11 +188,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards AutoSizeAxes = Axes.Y, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Padding = new MarginPadding - { - Horizontal = 10, - Vertical = 4 - }, Children = new Drawable[] { idleBottomContent = new FillFlowContainer @@ -388,30 +315,15 @@ namespace osu.Game.Beatmaps.Drawables.Cards { bool showDetails = IsHovered || Expanded.Value; - float targetWidth = width - height; - if (showDetails) - targetWidth = targetWidth - icon_area_width + CORNER_RADIUS; - + buttonContainer.ShowDetails.Value = showDetails; thumbnail.Dimmed.Value = showDetails; // Scale value is intentionally chosen to fit in the spacing of listing displays, as to not overlap horizontally with adjacent cards. // This avoids depth issues where a hovered (scaled) card to the right of another card would be beneath the card to the left. content.ScaleTo(Expanded.Value ? 1.03f : 1, 500, Easing.OutQuint); - mainContent.ResizeWidthTo(targetWidth, TRANSITION_DURATION, Easing.OutQuint); - mainContentBackground.Dimmed.Value = showDetails; - statisticsContainer.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint); - rightAreaBackground.FadeColour(downloadTracker.State.Value == DownloadState.LocallyAvailable ? colours.Lime0 : colourProvider.Background3, TRANSITION_DURATION, Easing.OutQuint); - rightAreaButtons.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint); - - foreach (var button in rightAreaButtons) - { - button.IdleColour = downloadTracker.State.Value != DownloadState.LocallyAvailable ? colourProvider.Light1 : colourProvider.Background3; - button.HoverColour = downloadTracker.State.Value != DownloadState.LocallyAvailable ? colourProvider.Content1 : colourProvider.Foreground1; - } - bool showProgress = downloadTracker.State.Value == DownloadState.Downloading || downloadTracker.State.Value == DownloadState.Importing; idleBottomContent.FadeTo(showProgress ? 0 : 1, TRANSITION_DURATION, Easing.OutQuint); diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 328294a323..fb498d643a 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -7,11 +7,9 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; -using osu.Game.Beatmaps.Drawables.Cards.Buttons; using osu.Game.Beatmaps.Drawables.Cards.Statistics; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -23,7 +21,6 @@ using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; using osuTK; using osu.Game.Resources.Localisation.Web; -using DownloadButton = osu.Game.Beatmaps.Drawables.Cards.Buttons.DownloadButton; namespace osu.Game.Beatmaps.Drawables.Cards { @@ -31,7 +28,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards { private const float width = 475; private const float height = 140; - private const float icon_area_width = 30; public Bindable Expanded { get; } = new BindableBool(); @@ -44,20 +40,13 @@ namespace osu.Game.Beatmaps.Drawables.Cards private readonly BeatmapCardContent content; private BeatmapCardThumbnail thumbnail = null!; + private CollapsibleButtonContainer buttonContainer = null!; - private Container rightAreaBackground = null!; - private Container rightAreaButtons = null!; - - private Container mainContent = null!; - private BeatmapCardContentBackground mainContentBackground = null!; private GridContainer statisticsContainer = null!; private FillFlowContainer idleBottomContent = null!; private BeatmapCardDownloadProgressBar downloadProgressBar = null!; - [Resolved] - private OsuColour colours { get; set; } = null!; - [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -88,21 +77,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards Children = new Drawable[] { downloadTracker, - rightAreaBackground = new Container - { - RelativeSizeAxes = Axes.Y, - Width = icon_area_width + 2 * BeatmapCard.CORNER_RADIUS, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - // workaround for masking artifacts at the top & bottom of card, - // which become especially visible on downloaded beatmaps (when the icon area has a lime background). - Padding = new MarginPadding { Vertical = 1 }, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Colour4.White - }, - }, thumbnail = new BeatmapCardThumbnail(beatmapSet) { Name = @"Left (icon) area", @@ -116,61 +90,19 @@ namespace osu.Game.Beatmaps.Drawables.Cards Spacing = new Vector2(1) } }, - new Container + buttonContainer = new CollapsibleButtonContainer(beatmapSet) { - Name = @"Right (button) area", - Width = 30, - RelativeSizeAxes = Axes.Y, - Origin = Anchor.TopRight, - Anchor = Anchor.TopRight, - Padding = new MarginPadding { Vertical = 35 }, - Child = rightAreaButtons = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new BeatmapCardIconButton[] - { - new FavouriteButton(beatmapSet) - { - Current = favouriteState, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre - }, - new DownloadButton(beatmapSet) - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - State = { BindTarget = downloadTracker.State } - }, - new GoToBeatmapButton(beatmapSet) - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - State = { BindTarget = downloadTracker.State } - } - } - } - }, - mainContent = new Container - { - Name = @"Main content", X = height - BeatmapCard.CORNER_RADIUS, - Height = height, - CornerRadius = BeatmapCard.CORNER_RADIUS, - Masking = true, + Width = width - height + BeatmapCard.CORNER_RADIUS, + FavouriteState = { BindTarget = favouriteState }, + ButtonsCollapsedWidth = BeatmapCard.CORNER_RADIUS, + ButtonsExpandedWidth = 30, + ButtonsPadding = new MarginPadding { Vertical = 35 }, Children = new Drawable[] { - mainContentBackground = new BeatmapCardContentBackground(beatmapSet) - { - RelativeSizeAxes = Axes.Both, - }, new FillFlowContainer { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Horizontal = 10, - Vertical = 4 - }, Direction = FillDirection.Vertical, Children = new Drawable[] { @@ -248,11 +180,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards AutoSizeAxes = Axes.Y, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Padding = new MarginPadding - { - Horizontal = 10, - Vertical = 4 - }, Children = new Drawable[] { idleBottomContent = new FillFlowContainer @@ -414,28 +341,13 @@ namespace osu.Game.Beatmaps.Drawables.Cards { bool showDetails = IsHovered || Expanded.Value; - float targetWidth = width - height; - if (showDetails) - targetWidth = targetWidth - icon_area_width + BeatmapCard.CORNER_RADIUS; - + buttonContainer.ShowDetails.Value = showDetails; thumbnail.Dimmed.Value = showDetails; // Scale value is intentionally chosen to fit in the spacing of listing displays, as to not overlap horizontally with adjacent cards. // This avoids depth issues where a hovered (scaled) card to the right of another card would be beneath the card to the left. content.ScaleTo(Expanded.Value ? 1.03f : 1, 500, Easing.OutQuint); - mainContent.ResizeWidthTo(targetWidth, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); - mainContentBackground.Dimmed.Value = showDetails; - - rightAreaBackground.FadeColour(downloadTracker.State.Value == DownloadState.LocallyAvailable ? colours.Lime0 : colourProvider.Background3, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); - rightAreaButtons.FadeTo(showDetails ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); - - foreach (var button in rightAreaButtons) - { - button.IdleColour = downloadTracker.State.Value != DownloadState.LocallyAvailable ? colourProvider.Light1 : colourProvider.Background3; - button.HoverColour = downloadTracker.State.Value != DownloadState.LocallyAvailable ? colourProvider.Content1 : colourProvider.Foreground1; - } - bool showProgress = downloadTracker.State.Value == DownloadState.Downloading || downloadTracker.State.Value == DownloadState.Importing; idleBottomContent.FadeTo(showProgress ? 0 : 1, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); diff --git a/osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainer.cs b/osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainer.cs new file mode 100644 index 0000000000..3a2cb80a8d --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainer.cs @@ -0,0 +1,184 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps.Drawables.Cards.Buttons; +using osu.Game.Graphics; +using osu.Game.Online; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; + +namespace osu.Game.Beatmaps.Drawables.Cards +{ + public class CollapsibleButtonContainer : Container + { + public Bindable ShowDetails = new Bindable(); + public Bindable FavouriteState = new Bindable(); + + private readonly BeatmapDownloadTracker downloadTracker; + + private float buttonsExpandedWidth; + + public float ButtonsExpandedWidth + { + get => buttonsExpandedWidth; + set + { + buttonsExpandedWidth = value; + buttonArea.Width = value; + if (IsLoaded) + updateState(); + } + } + + private float buttonsCollapsedWidth; + + public float ButtonsCollapsedWidth + { + get => buttonsCollapsedWidth; + set + { + buttonsCollapsedWidth = value; + if (IsLoaded) + updateState(); + } + } + + public MarginPadding ButtonsPadding + { + get => buttons.Padding; + set => buttons.Padding = value; + } + + protected override Container Content => mainContent; + + private readonly Container background; + + private readonly Container buttonArea; + private readonly Container buttons; + + private readonly Container mainArea; + private readonly Container mainContent; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + public CollapsibleButtonContainer(APIBeatmapSet beatmapSet) + { + downloadTracker = new BeatmapDownloadTracker(beatmapSet); + + RelativeSizeAxes = Axes.Y; + Masking = true; + CornerRadius = BeatmapCard.CORNER_RADIUS; + + InternalChildren = new Drawable[] + { + downloadTracker, + background = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + // workaround for masking artifacts at the top & bottom of card, + // which become especially visible on downloaded beatmaps (when the icon area has a lime background). + Padding = new MarginPadding { Vertical = 1 }, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.White + }, + }, + buttonArea = new Container + { + Name = @"Right (button) area", + RelativeSizeAxes = Axes.Y, + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + Child = buttons = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new BeatmapCardIconButton[] + { + new FavouriteButton(beatmapSet) + { + Current = FavouriteState, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre + }, + new DownloadButton(beatmapSet) + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + State = { BindTarget = downloadTracker.State } + }, + new GoToBeatmapButton(beatmapSet) + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + State = { BindTarget = downloadTracker.State } + } + } + } + }, + mainArea = new Container + { + Name = @"Main content", + RelativeSizeAxes = Axes.Y, + CornerRadius = BeatmapCard.CORNER_RADIUS, + Masking = true, + Children = new Drawable[] + { + new BeatmapCardContentBackground(beatmapSet) + { + RelativeSizeAxes = Axes.Both, + Dimmed = { BindTarget = ShowDetails } + }, + mainContent = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Horizontal = 10, + Vertical = 4 + }, + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + downloadTracker.State.BindValueChanged(_ => updateState()); + ShowDetails.BindValueChanged(_ => updateState(), true); + FinishTransforms(true); + } + + private void updateState() + { + float targetWidth = Width - (ShowDetails.Value ? ButtonsExpandedWidth : ButtonsCollapsedWidth); + + mainArea.ResizeWidthTo(targetWidth, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + + background.FadeColour(downloadTracker.State.Value == DownloadState.LocallyAvailable ? colours.Lime0 : colourProvider.Background3, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + buttons.FadeTo(ShowDetails.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + + foreach (var button in buttons) + { + button.IdleColour = downloadTracker.State.Value != DownloadState.LocallyAvailable ? colourProvider.Light1 : colourProvider.Background3; + button.HoverColour = downloadTracker.State.Value != DownloadState.LocallyAvailable ? colourProvider.Content1 : colourProvider.Foreground1; + } + } + } +} From d6f60399342512250fb0c92b90d0c8e13d853eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 17 Dec 2021 13:27:11 +0100 Subject: [PATCH 13/61] Extract base class for beatmap cards --- .../Beatmaps/Drawables/Cards/BeatmapCard.cs | 101 +++++----------- .../Drawables/Cards/BeatmapCardBase.cs | 80 +++++++++++++ .../Drawables/Cards/BeatmapCardContent.cs | 14 +-- .../Cards/BeatmapCardContentBackground.cs | 4 +- .../Cards/BeatmapCardDownloadProgressBar.cs | 4 +- .../Drawables/Cards/BeatmapCardExtra.cs | 108 ++++++------------ .../Drawables/Cards/BeatmapCardThumbnail.cs | 4 +- .../Cards/Buttons/BeatmapCardIconButton.cs | 2 +- .../Drawables/Cards/Buttons/DownloadButton.cs | 4 +- .../Cards/Buttons/GoToBeatmapButton.cs | 2 +- .../Drawables/Cards/Buttons/PlayButton.cs | 2 +- .../Cards/CollapsibleButtonContainer.cs | 10 +- 12 files changed, 168 insertions(+), 167 deletions(-) create mode 100644 osu.Game/Beatmaps/Drawables/Cards/BeatmapCardBase.cs diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index 13c4cfe207..b0df2e40d3 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -5,18 +5,14 @@ using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps.Drawables.Cards.Statistics; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; @@ -25,21 +21,14 @@ using osu.Game.Resources.Localisation.Web; namespace osu.Game.Beatmaps.Drawables.Cards { - public class BeatmapCard : OsuClickableContainer + public class BeatmapCard : BeatmapCardBase { - public const float TRANSITION_DURATION = 400; - public const float CORNER_RADIUS = 10; - - public IBindable Expanded { get; } + protected override Drawable IdleContent => idleBottomContent; + protected override Drawable DownloadInProgressContent => downloadProgressBar; private const float width = 408; private const float height = 100; - private readonly APIBeatmapSet beatmapSet; - private readonly Bindable favouriteState; - - private readonly BeatmapDownloadTracker downloadTracker; - [Cached] private readonly BeatmapCardContent content; @@ -55,18 +44,13 @@ namespace osu.Game.Beatmaps.Drawables.Cards private OverlayColourProvider colourProvider { get; set; } = null!; public BeatmapCard(APIBeatmapSet beatmapSet, bool allowExpansion = true) - : base(HoverSampleSet.Submit) + : base(beatmapSet, allowExpansion) { - Expanded = new BindableBool { Disabled = !allowExpansion }; - - this.beatmapSet = beatmapSet; - favouriteState = new Bindable(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount)); - downloadTracker = new BeatmapDownloadTracker(beatmapSet); content = new BeatmapCardContent(height); } - [BackgroundDependencyLoader(true)] - private void load(BeatmapSetOverlay? beatmapSetOverlay) + [BackgroundDependencyLoader] + private void load() { Width = width; Height = height; @@ -75,15 +59,14 @@ namespace osu.Game.Beatmaps.Drawables.Cards GridContainer titleContainer = null!; GridContainer artistContainer = null!; - InternalChild = content.With(c => + Child = content.With(c => { c.MainContent = new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - downloadTracker, - thumbnail = new BeatmapCardThumbnail(beatmapSet) + thumbnail = new BeatmapCardThumbnail(BeatmapSet) { Name = @"Left (icon) area", Size = new Vector2(height), @@ -96,11 +79,11 @@ namespace osu.Game.Beatmaps.Drawables.Cards Spacing = new Vector2(1) } }, - buttonContainer = new CollapsibleButtonContainer(beatmapSet) + buttonContainer = new CollapsibleButtonContainer(BeatmapSet) { X = height - CORNER_RADIUS, Width = width - height + CORNER_RADIUS, - FavouriteState = { BindTarget = favouriteState }, + FavouriteState = { BindTarget = FavouriteState }, ButtonsCollapsedWidth = CORNER_RADIUS, ButtonsExpandedWidth = 30, ButtonsPadding = new MarginPadding { Vertical = 17.5f }, @@ -131,7 +114,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards { new OsuSpriteText { - Text = new RomanisableString(beatmapSet.TitleUnicode, beatmapSet.Title), + Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title), Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold), RelativeSizeAxes = Axes.X, Truncate = true @@ -177,7 +160,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards d.AutoSizeAxes = Axes.Both; d.Margin = new MarginPadding { Top = 2 }; d.AddText("mapped by ", t => t.Colour = colourProvider.Content2); - d.AddUserLink(beatmapSet.Author); + d.AddUserLink(BeatmapSet.Author); }), } }, @@ -209,7 +192,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards AlwaysPresent = true, ChildrenEnumerable = createStatistics() }, - new BeatmapCardExtraInfoRow(beatmapSet) + new BeatmapCardExtraInfoRow(BeatmapSet) } }, downloadProgressBar = new BeatmapCardDownloadProgressBar @@ -218,8 +201,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards Height = 6, Anchor = Anchor.Centre, Origin = Anchor.Centre, - State = { BindTarget = downloadTracker.State }, - Progress = { BindTarget = downloadTracker.Progress } + State = { BindTarget = DownloadTracker.State }, + Progress = { BindTarget = DownloadTracker.Progress } } } } @@ -232,18 +215,18 @@ namespace osu.Game.Beatmaps.Drawables.Cards RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = 10, Vertical = 13 }, - Child = new BeatmapCardDifficultyList(beatmapSet) + Child = new BeatmapCardDifficultyList(BeatmapSet) }; c.Expanded.BindTarget = Expanded; }); - if (beatmapSet.HasVideo) + if (BeatmapSet.HasVideo) leftIconArea.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) }); - if (beatmapSet.HasStoryboard) + if (BeatmapSet.HasStoryboard) leftIconArea.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) }); - if (beatmapSet.HasExplicitContent) + if (BeatmapSet.HasExplicitContent) { titleContainer.Content[0][1] = new ExplicitContentBeatmapPill { @@ -253,7 +236,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards }; } - if (beatmapSet.TrackId != null) + if (BeatmapSet.TrackId != null) { artistContainer.Content[0][1] = new FeaturedArtistBeatmapPill { @@ -262,57 +245,36 @@ namespace osu.Game.Beatmaps.Drawables.Cards Margin = new MarginPadding { Left = 5 } }; } - - Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmapSet.OnlineID); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - downloadTracker.State.BindValueChanged(_ => updateState()); - Expanded.BindValueChanged(_ => updateState(), true); - FinishTransforms(true); - } - - protected override bool OnHover(HoverEvent e) - { - updateState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateState(); - base.OnHoverLost(e); } private LocalisableString createArtistText() { - var romanisableArtist = new RomanisableString(beatmapSet.ArtistUnicode, beatmapSet.Artist); + var romanisableArtist = new RomanisableString(BeatmapSet.ArtistUnicode, BeatmapSet.Artist); return BeatmapsetsStrings.ShowDetailsByArtist(romanisableArtist); } private IEnumerable createStatistics() { - var hypesStatistic = HypesStatistic.CreateFor(beatmapSet); + var hypesStatistic = HypesStatistic.CreateFor(BeatmapSet); if (hypesStatistic != null) yield return hypesStatistic; - var nominationsStatistic = NominationsStatistic.CreateFor(beatmapSet); + var nominationsStatistic = NominationsStatistic.CreateFor(BeatmapSet); if (nominationsStatistic != null) yield return nominationsStatistic; - yield return new FavouritesStatistic(beatmapSet) { Current = favouriteState }; - yield return new PlayCountStatistic(beatmapSet); + yield return new FavouritesStatistic(BeatmapSet) { Current = FavouriteState }; + yield return new PlayCountStatistic(BeatmapSet); - var dateStatistic = BeatmapCardDateStatistic.CreateFor(beatmapSet); + var dateStatistic = BeatmapCardDateStatistic.CreateFor(BeatmapSet); if (dateStatistic != null) yield return dateStatistic; } - private void updateState() + protected override void UpdateState() { + base.UpdateState(); + bool showDetails = IsHovered || Expanded.Value; buttonContainer.ShowDetails.Value = showDetails; @@ -323,11 +285,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards content.ScaleTo(Expanded.Value ? 1.03f : 1, 500, Easing.OutQuint); statisticsContainer.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint); - - bool showProgress = downloadTracker.State.Value == DownloadState.Downloading || downloadTracker.State.Value == DownloadState.Importing; - - idleBottomContent.FadeTo(showProgress ? 0 : 1, TRANSITION_DURATION, Easing.OutQuint); - downloadProgressBar.FadeTo(showProgress ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardBase.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardBase.cs new file mode 100644 index 0000000000..77df0809bc --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardBase.cs @@ -0,0 +1,80 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; + +namespace osu.Game.Beatmaps.Drawables.Cards +{ + public abstract class BeatmapCardBase : OsuClickableContainer + { + public const float TRANSITION_DURATION = 400; + public const float CORNER_RADIUS = 10; + + public IBindable Expanded { get; } + + protected readonly APIBeatmapSet BeatmapSet; + protected readonly Bindable FavouriteState; + + protected abstract Drawable IdleContent { get; } + protected abstract Drawable DownloadInProgressContent { get; } + + protected readonly BeatmapDownloadTracker DownloadTracker; + + protected BeatmapCardBase(APIBeatmapSet beatmapSet, bool allowExpansion = true) + : base(HoverSampleSet.Submit) + { + Expanded = new BindableBool { Disabled = !allowExpansion }; + + BeatmapSet = beatmapSet; + FavouriteState = new Bindable(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount)); + DownloadTracker = new BeatmapDownloadTracker(beatmapSet); + } + + [BackgroundDependencyLoader(true)] + private void load(BeatmapSetOverlay? beatmapSetOverlay) + { + Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(BeatmapSet.OnlineID); + + AddInternal(DownloadTracker); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + DownloadTracker.State.BindValueChanged(_ => UpdateState()); + Expanded.BindValueChanged(_ => UpdateState(), true); + FinishTransforms(true); + } + + protected override bool OnHover(HoverEvent e) + { + UpdateState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + UpdateState(); + base.OnHoverLost(e); + } + + protected virtual void UpdateState() + { + bool showProgress = DownloadTracker.State.Value == DownloadState.Downloading || DownloadTracker.State.Value == DownloadState.Importing; + + IdleContent.FadeTo(showProgress ? 0 : 1, TRANSITION_DURATION, Easing.OutQuint); + DownloadInProgressContent.FadeTo(showProgress ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint); + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs index 286e03e700..e16421503c 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs @@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - CornerRadius = BeatmapCard.CORNER_RADIUS, + CornerRadius = BeatmapCardBase.CORNER_RADIUS, Masking = true, Unhovered = _ => updateFromHoverChange(), Children = new Drawable[] @@ -67,7 +67,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards { RelativeSizeAxes = Axes.X, Height = height, - CornerRadius = BeatmapCard.CORNER_RADIUS, + CornerRadius = BeatmapCardBase.CORNER_RADIUS, Masking = true, }, dropdownContent = new HoverHandlingContainer @@ -91,7 +91,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards borderContainer = new Container { RelativeSizeAxes = Axes.Both, - CornerRadius = BeatmapCard.CORNER_RADIUS, + CornerRadius = BeatmapCardBase.CORNER_RADIUS, Masking = true, BorderThickness = 3, Child = new Box @@ -139,9 +139,9 @@ namespace osu.Game.Beatmaps.Drawables.Cards private void updateState() { - background.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); - dropdownContent.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); - borderContainer.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + background.FadeTo(Expanded.Value ? 1 : 0, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + dropdownContent.FadeTo(Expanded.Value ? 1 : 0, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + borderContainer.FadeTo(Expanded.Value ? 1 : 0, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); content.TweenEdgeEffectTo(new EdgeEffectParameters { @@ -150,7 +150,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards Radius = 10, Colour = Colour4.Black.Opacity(Expanded.Value ? 0.3f : 0f), Hollow = true, - }, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + }, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); } private class ExpandedContentScrollContainer : OsuScrollContainer diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContentBackground.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContentBackground.cs index 392f5d1bfa..6388e1698c 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContentBackground.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContentBackground.cs @@ -62,10 +62,10 @@ namespace osu.Game.Beatmaps.Drawables.Cards private void updateState() => Schedule(() => { - background.FadeColour(Dimmed.Value ? colourProvider.Background4 : colourProvider.Background2, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + background.FadeColour(Dimmed.Value ? colourProvider.Background4 : colourProvider.Background2, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); var gradient = ColourInfo.GradientHorizontal(Colour4.White.Opacity(0), Colour4.White.Opacity(0.2f)); - cover.FadeColour(gradient, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + cover.FadeColour(gradient, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); }); } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDownloadProgressBar.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDownloadProgressBar.cs index ffb4e0c540..b80c5221ab 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDownloadProgressBar.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDownloadProgressBar.cs @@ -82,14 +82,14 @@ namespace osu.Game.Beatmaps.Drawables.Cards break; case DownloadState.Importing: - foregroundFill.FadeColour(colours.Yellow, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + foregroundFill.FadeColour(colours.Yellow, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); break; } } private void progressChanged() { - foreground.ResizeWidthTo((float)progress.Value, progress.Value > 0 ? BeatmapCard.TRANSITION_DURATION : 0, Easing.OutQuint); + foreground.ResizeWidthTo((float)progress.Value, progress.Value > 0 ? BeatmapCardBase.TRANSITION_DURATION : 0, Easing.OutQuint); } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index fb498d643a..39acbb352d 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -4,18 +4,14 @@ #nullable enable using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps.Drawables.Cards.Statistics; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; @@ -24,18 +20,14 @@ using osu.Game.Resources.Localisation.Web; namespace osu.Game.Beatmaps.Drawables.Cards { - public class BeatmapCardExtra : OsuClickableContainer + public class BeatmapCardExtra : BeatmapCardBase { + protected override Drawable IdleContent => idleBottomContent; + protected override Drawable DownloadInProgressContent => downloadProgressBar; + private const float width = 475; private const float height = 140; - public Bindable Expanded { get; } = new BindableBool(); - - private readonly APIBeatmapSet beatmapSet; - private readonly Bindable favouriteState; - - private readonly BeatmapDownloadTracker downloadTracker; - [Cached] private readonly BeatmapCardContent content; @@ -50,12 +42,9 @@ namespace osu.Game.Beatmaps.Drawables.Cards [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - public BeatmapCardExtra(APIBeatmapSet beatmapSet) - : base(HoverSampleSet.Submit) + public BeatmapCardExtra(APIBeatmapSet beatmapSet, bool allowExpansion = true) + : base(beatmapSet, allowExpansion) { - this.beatmapSet = beatmapSet; - favouriteState = new Bindable(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount)); - downloadTracker = new BeatmapDownloadTracker(beatmapSet); content = new BeatmapCardContent(height); } @@ -69,19 +58,18 @@ namespace osu.Game.Beatmaps.Drawables.Cards GridContainer titleContainer = null!; GridContainer artistContainer = null!; - InternalChild = content.With(c => + Child = content.With(c => { c.MainContent = new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - downloadTracker, - thumbnail = new BeatmapCardThumbnail(beatmapSet) + thumbnail = new BeatmapCardThumbnail(BeatmapSet) { Name = @"Left (icon) area", Size = new Vector2(height), - Padding = new MarginPadding { Right = BeatmapCard.CORNER_RADIUS }, + Padding = new MarginPadding { Right = CORNER_RADIUS }, Child = leftIconArea = new FillFlowContainer { Margin = new MarginPadding(5), @@ -90,12 +78,12 @@ namespace osu.Game.Beatmaps.Drawables.Cards Spacing = new Vector2(1) } }, - buttonContainer = new CollapsibleButtonContainer(beatmapSet) + buttonContainer = new CollapsibleButtonContainer(BeatmapSet) { - X = height - BeatmapCard.CORNER_RADIUS, - Width = width - height + BeatmapCard.CORNER_RADIUS, - FavouriteState = { BindTarget = favouriteState }, - ButtonsCollapsedWidth = BeatmapCard.CORNER_RADIUS, + X = height - CORNER_RADIUS, + Width = width - height + CORNER_RADIUS, + FavouriteState = { BindTarget = FavouriteState }, + ButtonsCollapsedWidth = CORNER_RADIUS, ButtonsExpandedWidth = 30, ButtonsPadding = new MarginPadding { Vertical = 35 }, Children = new Drawable[] @@ -125,7 +113,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards { new OsuSpriteText { - Text = new RomanisableString(beatmapSet.TitleUnicode, beatmapSet.Title), + Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title), Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold), RelativeSizeAxes = Axes.X, Truncate = true @@ -166,7 +154,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards { RelativeSizeAxes = Axes.X, Truncate = true, - Text = beatmapSet.Source, + Text = BeatmapSet.Source, Shadow = false, Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold), Colour = colourProvider.Content2 @@ -200,7 +188,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards d.AutoSizeAxes = Axes.Both; d.Margin = new MarginPadding { Top = 2 }; d.AddText("mapped by ", t => t.Colour = colourProvider.Content2); - d.AddUserLink(beatmapSet.Author); + d.AddUserLink(BeatmapSet.Author); }), statisticsContainer = new GridContainer { @@ -223,7 +211,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards new Drawable[3] } }, - new BeatmapCardExtraInfoRow(beatmapSet) + new BeatmapCardExtraInfoRow(BeatmapSet) } }, downloadProgressBar = new BeatmapCardDownloadProgressBar @@ -232,8 +220,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards Height = 6, Anchor = Anchor.Centre, Origin = Anchor.Centre, - State = { BindTarget = downloadTracker.State }, - Progress = { BindTarget = downloadTracker.Progress } + State = { BindTarget = DownloadTracker.State }, + Progress = { BindTarget = DownloadTracker.Progress } } } } @@ -246,18 +234,18 @@ namespace osu.Game.Beatmaps.Drawables.Cards RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Horizontal = 10, Vertical = 13 }, - Child = new BeatmapCardDifficultyList(beatmapSet) + Child = new BeatmapCardDifficultyList(BeatmapSet) }; c.Expanded.BindTarget = Expanded; }); - if (beatmapSet.HasVideo) + if (BeatmapSet.HasVideo) leftIconArea.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) }); - if (beatmapSet.HasStoryboard) + if (BeatmapSet.HasStoryboard) leftIconArea.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) }); - if (beatmapSet.HasExplicitContent) + if (BeatmapSet.HasExplicitContent) { titleContainer.Content[0][1] = new ExplicitContentBeatmapPill { @@ -267,7 +255,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards }; } - if (beatmapSet.TrackId != null) + if (BeatmapSet.TrackId != null) { artistContainer.Content[0][1] = new FeaturedArtistBeatmapPill { @@ -279,33 +267,12 @@ namespace osu.Game.Beatmaps.Drawables.Cards createStatistics(); - Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmapSet.OnlineID); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - downloadTracker.State.BindValueChanged(_ => updateState()); - Expanded.BindValueChanged(_ => updateState(), true); - FinishTransforms(true); - } - - protected override bool OnHover(HoverEvent e) - { - updateState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateState(); - base.OnHoverLost(e); + Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(BeatmapSet.OnlineID); } private LocalisableString createArtistText() { - var romanisableArtist = new RomanisableString(beatmapSet.ArtistUnicode, beatmapSet.Artist); + var romanisableArtist = new RomanisableString(BeatmapSet.ArtistUnicode, BeatmapSet.Artist); return BeatmapsetsStrings.ShowDetailsByArtist(romanisableArtist); } @@ -317,28 +284,30 @@ namespace osu.Game.Beatmaps.Drawables.Cards return original; } - statisticsContainer.Content[0][0] = withMargin(new FavouritesStatistic(beatmapSet) + statisticsContainer.Content[0][0] = withMargin(new FavouritesStatistic(BeatmapSet) { - Current = favouriteState, + Current = FavouriteState, }); - statisticsContainer.Content[1][0] = withMargin(new PlayCountStatistic(beatmapSet)); + statisticsContainer.Content[1][0] = withMargin(new PlayCountStatistic(BeatmapSet)); - var hypesStatistic = HypesStatistic.CreateFor(beatmapSet); + var hypesStatistic = HypesStatistic.CreateFor(BeatmapSet); if (hypesStatistic != null) statisticsContainer.Content[0][1] = withMargin(hypesStatistic); - var nominationsStatistic = NominationsStatistic.CreateFor(beatmapSet); + var nominationsStatistic = NominationsStatistic.CreateFor(BeatmapSet); if (nominationsStatistic != null) statisticsContainer.Content[1][1] = withMargin(nominationsStatistic); - var dateStatistic = BeatmapCardDateStatistic.CreateFor(beatmapSet); + var dateStatistic = BeatmapCardDateStatistic.CreateFor(BeatmapSet); if (dateStatistic != null) statisticsContainer.Content[0][2] = withMargin(dateStatistic); } - private void updateState() + protected override void UpdateState() { + base.UpdateState(); + bool showDetails = IsHovered || Expanded.Value; buttonContainer.ShowDetails.Value = showDetails; @@ -347,11 +316,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards // Scale value is intentionally chosen to fit in the spacing of listing displays, as to not overlap horizontally with adjacent cards. // This avoids depth issues where a hovered (scaled) card to the right of another card would be beneath the card to the left. content.ScaleTo(Expanded.Value ? 1.03f : 1, 500, Easing.OutQuint); - - bool showProgress = downloadTracker.State.Value == DownloadState.Downloading || downloadTracker.State.Value == DownloadState.Importing; - - idleBottomContent.FadeTo(showProgress ? 0 : 1, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); - downloadProgressBar.FadeTo(showProgress ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs index f11a5916e1..e6b305552a 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs @@ -88,8 +88,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards { bool shouldDim = Dimmed.Value || playButton.Playing.Value; - playButton.FadeTo(shouldDim ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); - cover.FadeColour(shouldDim ? OsuColour.Gray(0.2f) : Color4.White, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + playButton.FadeTo(shouldDim ? 1 : 0, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + cover.FadeColour(shouldDim ? OsuColour.Gray(0.2f) : Color4.White, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/Buttons/BeatmapCardIconButton.cs b/osu.Game/Beatmaps/Drawables/Cards/Buttons/BeatmapCardIconButton.cs index e362e3abeb..02c9ea640a 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/Buttons/BeatmapCardIconButton.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/Buttons/BeatmapCardIconButton.cs @@ -115,7 +115,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Buttons bool isHovered = IsHovered && Enabled.Value; content.ScaleTo(isHovered ? 1.2f : 1, 500, Easing.OutQuint); - content.FadeColour(isHovered ? HoverColour : IdleColour, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + content.FadeColour(isHovered ? HoverColour : IdleColour, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/Buttons/DownloadButton.cs b/osu.Game/Beatmaps/Drawables/Cards/Buttons/DownloadButton.cs index c94e335e8f..d7ba4af21e 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/Buttons/DownloadButton.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/Buttons/DownloadButton.cs @@ -69,7 +69,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Buttons case DownloadState.LocallyAvailable: Action = null; TooltipText = string.Empty; - this.FadeOut(BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + this.FadeOut(BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); break; case DownloadState.NotDownloaded: @@ -81,7 +81,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Buttons } Action = () => beatmaps.Download(beatmapSet, preferNoVideo.Value); - this.FadeIn(BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + this.FadeIn(BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); spinner.Hide(); Icon.Show(); diff --git a/osu.Game/Beatmaps/Drawables/Cards/Buttons/GoToBeatmapButton.cs b/osu.Game/Beatmaps/Drawables/Cards/Buttons/GoToBeatmapButton.cs index 9a6a3c01b7..b039eb6f10 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/Buttons/GoToBeatmapButton.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/Buttons/GoToBeatmapButton.cs @@ -43,7 +43,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Buttons private void updateState() { - this.FadeTo(state.Value == DownloadState.LocallyAvailable ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + this.FadeTo(state.Value == DownloadState.LocallyAvailable ? 1 : 0, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs b/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs index f7bab26666..6d66d5001a 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/Buttons/PlayButton.cs @@ -141,7 +141,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Buttons private void toggleLoading(bool loading) { Enabled.Value = !loading; - icon.FadeTo(loading ? 0 : 1, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + icon.FadeTo(loading ? 0 : 1, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); loadingSpinner.State.Value = loading ? Visibility.Visible : Visibility.Hidden; } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainer.cs b/osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainer.cs index 3a2cb80a8d..e1a630182f 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainer.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/CollapsibleButtonContainer.cs @@ -78,7 +78,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards RelativeSizeAxes = Axes.Y; Masking = true; - CornerRadius = BeatmapCard.CORNER_RADIUS; + CornerRadius = BeatmapCardBase.CORNER_RADIUS; InternalChildren = new Drawable[] { @@ -133,7 +133,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards { Name = @"Main content", RelativeSizeAxes = Axes.Y, - CornerRadius = BeatmapCard.CORNER_RADIUS, + CornerRadius = BeatmapCardBase.CORNER_RADIUS, Masking = true, Children = new Drawable[] { @@ -169,10 +169,10 @@ namespace osu.Game.Beatmaps.Drawables.Cards { float targetWidth = Width - (ShowDetails.Value ? ButtonsExpandedWidth : ButtonsCollapsedWidth); - mainArea.ResizeWidthTo(targetWidth, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + mainArea.ResizeWidthTo(targetWidth, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); - background.FadeColour(downloadTracker.State.Value == DownloadState.LocallyAvailable ? colours.Lime0 : colourProvider.Background3, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); - buttons.FadeTo(ShowDetails.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + background.FadeColour(downloadTracker.State.Value == DownloadState.LocallyAvailable ? colours.Lime0 : colourProvider.Background3, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + buttons.FadeTo(ShowDetails.Value ? 1 : 0, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); foreach (var button in buttons) { From 33e930f47786c5edad0d003fdee13aaf10f079cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 17 Dec 2021 13:29:20 +0100 Subject: [PATCH 14/61] Move scale-on-expand logic to `BeatmapCardContent` --- osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs | 4 ---- osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs | 4 ++++ osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs | 4 ---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index b0df2e40d3..a3b4c4c86c 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -280,10 +280,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards buttonContainer.ShowDetails.Value = showDetails; thumbnail.Dimmed.Value = showDetails; - // Scale value is intentionally chosen to fit in the spacing of listing displays, as to not overlap horizontally with adjacent cards. - // This avoids depth issues where a hovered (scaled) card to the right of another card would be beneath the card to the left. - content.ScaleTo(Expanded.Value ? 1.03f : 1, 500, Easing.OutQuint); - statisticsContainer.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint); } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs index e16421503c..9c43c16100 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs @@ -139,6 +139,10 @@ namespace osu.Game.Beatmaps.Drawables.Cards private void updateState() { + // Scale value is intentionally chosen to fit in the spacing of listing displays, as to not overlap horizontally with adjacent cards. + // This avoids depth issues where a hovered (scaled) card to the right of another card would be beneath the card to the left. + this.ScaleTo(Expanded.Value ? 1.03f : 1, 500, Easing.OutQuint); + background.FadeTo(Expanded.Value ? 1 : 0, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); dropdownContent.FadeTo(Expanded.Value ? 1 : 0, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); borderContainer.FadeTo(Expanded.Value ? 1 : 0, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 39acbb352d..308cba9f0f 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -312,10 +312,6 @@ namespace osu.Game.Beatmaps.Drawables.Cards buttonContainer.ShowDetails.Value = showDetails; thumbnail.Dimmed.Value = showDetails; - - // Scale value is intentionally chosen to fit in the spacing of listing displays, as to not overlap horizontally with adjacent cards. - // This avoids depth issues where a hovered (scaled) card to the right of another card would be beneath the card to the left. - content.ScaleTo(Expanded.Value ? 1.03f : 1, 500, Easing.OutQuint); } } } From 04cfae9bdeb525855a34527a217e8b2cba34b115 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 18 Dec 2021 16:26:15 +0900 Subject: [PATCH 15/61] Fix "Random Skin" text not showing up correctly --- osu.Game/Database/RealmLiveUnmanaged.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Database/RealmLiveUnmanaged.cs b/osu.Game/Database/RealmLiveUnmanaged.cs index 5a69898206..ea50ccc1ff 100644 --- a/osu.Game/Database/RealmLiveUnmanaged.cs +++ b/osu.Game/Database/RealmLiveUnmanaged.cs @@ -26,6 +26,8 @@ namespace osu.Game.Database public bool Equals(ILive? other) => ID == other?.ID; + public override string ToString() => Value.ToString(); + public Guid ID => Value.ID; public void PerformRead(Action perform) => perform(Value); From c11217755987abbe9ca0c63ad832f6839463647e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Dec 2021 12:48:09 +0100 Subject: [PATCH 16/61] Bring profile header test in line with modern conventions - Removed online code that didn't work anyway after the introduction of the development web instance. - Removed some weird test steps. - Fixed online/offline test steps not working at all due to identical user ID. --- .../Online/TestSceneUserProfileHeader.cs | 73 +++++-------------- 1 file changed, 20 insertions(+), 53 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index 76997bded7..c319d2f7de 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -2,85 +2,52 @@ // See the LICENCE file in the repository root for full licence text. using System; +using NUnit.Framework; using osu.Framework.Allocation; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; +using osu.Framework.Testing; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Profile; -using osu.Game.Users; namespace osu.Game.Tests.Visual.Online { public class TestSceneUserProfileHeader : OsuTestScene { - protected override bool UseOnlineAPI => true; - [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); - [Resolved] - private IAPIProvider api { get; set; } + private ProfileHeader header; - private readonly ProfileHeader header; - - public TestSceneUserProfileHeader() + [SetUpSteps] + public void SetUpSteps() { - header = new ProfileHeader(); - Add(header); + AddStep("create header", () => Child = header = new ProfileHeader()); + } - AddStep("Show test dummy", () => header.User.Value = TestSceneUserProfileOverlay.TEST_USER); + [Test] + public void TestBasic() + { + AddStep("Show example user", () => header.User.Value = TestSceneUserProfileOverlay.TEST_USER); + } - AddStep("Show null dummy", () => header.User.Value = new APIUser - { - Username = "Null" - }); - - AddStep("Show online dummy", () => header.User.Value = new APIUser + [Test] + public void TestOnlineState() + { + AddStep("Show online user", () => header.User.Value = new APIUser { + Id = 1001, Username = "IAmOnline", LastVisit = DateTimeOffset.Now, IsOnline = true, }); - AddStep("Show offline dummy", () => header.User.Value = new APIUser + AddStep("Show offline user", () => header.User.Value = new APIUser { + Id = 1002, Username = "IAmOffline", - LastVisit = DateTimeOffset.Now, + LastVisit = DateTimeOffset.Now.AddDays(-10), IsOnline = false, }); - - addOnlineStep("Show ppy", new APIUser - { - Username = @"peppy", - Id = 2, - IsSupporter = true, - Country = new Country { FullName = @"Australia", FlagName = @"AU" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg" - }); - - addOnlineStep("Show flyte", new APIUser - { - Username = @"flyte", - Id = 3103765, - Country = new Country { FullName = @"Japan", FlagName = @"JP" }, - CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg" - }); - } - - private void addOnlineStep(string name, APIUser fallback) - { - AddStep(name, () => - { - if (api.IsLoggedIn) - { - var request = new GetUserRequest(fallback.Id); - request.Success += user => header.User.Value = user; - api.Queue(request); - } - else - header.User.Value = fallback; - }); } } } From 097402677d131f96aca3031c13876a5d927bcfb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Dec 2021 12:55:24 +0100 Subject: [PATCH 17/61] Add test for ranked/unranked user display --- .../Online/TestSceneUserProfileHeader.cs | 39 +++++++++++++++++++ .../Online/TestSceneUserProfileOverlay.cs | 1 + osu.Game/Users/UserStatistics.cs | 3 ++ 3 files changed, 43 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index c319d2f7de..dda9543159 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -2,12 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Testing; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Profile; +using osu.Game.Users; namespace osu.Game.Tests.Visual.Online { @@ -49,5 +51,42 @@ namespace osu.Game.Tests.Visual.Online IsOnline = false, }); } + + [Test] + public void TestRankedState() + { + AddStep("Show ranked user", () => header.User.Value = new APIUser + { + Id = 2001, + Username = "RankedUser", + Statistics = new UserStatistics + { + IsRanked = true, + GlobalRank = 15000, + CountryRank = 1500, + RankHistory = new APIRankHistory + { + Mode = @"osu", + Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() + }, + } + }); + + AddStep("Show unranked user", () => header.User.Value = new APIUser + { + Id = 2002, + Username = "UnrankedUser", + Statistics = new UserStatistics + { + IsRanked = false, + // web will sometimes return non-empty rank history even for unranked users. + RankHistory = new APIRankHistory + { + Mode = @"osu", + Data = Enumerable.Range(2345, 85).ToArray() + }, + } + }); + } } } diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs index 1c92bb1e38..78e2ceb45b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs @@ -29,6 +29,7 @@ namespace osu.Game.Tests.Visual.Online ProfileOrder = new[] { "me" }, Statistics = new UserStatistics { + IsRanked = true, GlobalRank = 2148, CountryRank = 1, PP = 4567.89m, diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs index c690447256..f8d26fe421 100644 --- a/osu.Game/Users/UserStatistics.cs +++ b/osu.Game/Users/UserStatistics.cs @@ -27,6 +27,9 @@ namespace osu.Game.Users public int Progress; } + [JsonProperty(@"is_ranked")] + public bool IsRanked; + [JsonProperty(@"global_rank")] public int? GlobalRank; From a6ccbafc77ead8416a8ea6f10407bd30db186046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 19 Dec 2021 13:19:00 +0100 Subject: [PATCH 18/61] Fix rank graph showing for unranked users --- osu.Game/Overlays/Profile/Header/Components/RankGraph.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index d6e515d8a1..d195babcbf 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -42,7 +42,9 @@ namespace osu.Game.Overlays.Profile.Header.Components private void updateStatistics(UserStatistics statistics) { - int[] userRanks = statistics?.RankHistory?.Data; + // checking both IsRanked and RankHistory is required. + // see https://github.com/ppy/osu-web/blob/154ceafba0f35a1dd935df53ec98ae2ea5615f9f/resources/assets/lib/profile-page/rank-chart.tsx#L46 + int[] userRanks = statistics?.IsRanked == true ? statistics.RankHistory?.Data : null; Data = userRanks?.Select((x, index) => new KeyValuePair(index, x)).Where(x => x.Value != 0).ToArray(); } From 1203ae5984ca1c399f5c876a2cee6b4900466e5f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 20 Dec 2021 11:49:39 +0900 Subject: [PATCH 19/61] Require installing .NET 5 in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 24b70b2de6..f18c5e76f9 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ You can see some examples of custom rulesets by visiting the [custom ruleset dir Please make sure you have the following prerequisites: -- A desktop platform with the [.NET 5.0 SDK](https://dotnet.microsoft.com/download) or higher installed. +- A desktop platform with the [.NET 5.0 SDK](https://dotnet.microsoft.com/download) installed. - When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). - When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/). - When running on Linux, please have a system-wide FFmpeg installation available to support video decoding. From 11f3ec0cb5821311ae5b5f8df1231925c3bd7efe Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 20 Dec 2021 12:44:22 +0900 Subject: [PATCH 20/61] Wait for previous screen to be hidden in test --- .../Visual/Background/TestSceneBackgroundScreenDefault.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs index 3211405670..844fe7705a 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneBackgroundScreenDefault.cs @@ -85,6 +85,7 @@ namespace osu.Game.Tests.Visual.Background // of note, this needs to be a type that doesn't match BackgroundScreenDefault else it is silently not pushed by the background stack. AddStep("push new background to stack", () => stack.Push(nestedScreen = new BackgroundScreenBeatmap(Beatmap.Value))); AddUntilStep("wait for screen to load", () => nestedScreen.IsLoaded && nestedScreen.IsCurrentScreen()); + AddUntilStep("previous background hidden", () => !screen.IsAlive); AddAssert("top level background hasn't changed yet", () => screen.CheckLastLoadChange() == null); From 9316abc2786dded8078cccb977d21a7058c5faa4 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 20 Dec 2021 13:10:13 +0900 Subject: [PATCH 21/61] Better fix for intermittent multiplayer tests --- .../Visual/Multiplayer/QueueModeTestScene.cs | 3 +-- .../Multiplayer/TestSceneMultiplayer.cs | 10 ++++---- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 2 +- .../Multiplayer/MultiplayerTestScene.cs | 2 +- .../Multiplayer/TestMultiplayerClient.cs | 12 +++++++--- .../Multiplayer/TestMultiplayerRoomManager.cs | 24 ++----------------- 6 files changed, 19 insertions(+), 34 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index 357db16e2c..83292c36bf 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs @@ -41,7 +41,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private TestMultiplayerScreenStack multiplayerScreenStack; protected TestMultiplayerClient Client => multiplayerScreenStack.Client; - protected TestMultiplayerRoomManager RoomManager => multiplayerScreenStack.RoomManager; [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); @@ -93,7 +92,7 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - AddUntilStep("wait for join", () => RoomManager.RoomJoined); + AddUntilStep("wait for join", () => Client.RoomJoined); } [Test] diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 710855a605..99411254a7 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -216,7 +216,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("Press select", () => InputManager.Key(Key.Enter)); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); - AddUntilStep("wait for join", () => roomManager.RoomJoined); + AddUntilStep("wait for join", () => client.RoomJoined); } [Test] @@ -295,7 +295,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("join room", () => InputManager.Key(Key.Enter)); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); - AddUntilStep("wait for join", () => roomManager.RoomJoined); + AddUntilStep("wait for join", () => client.RoomJoined); AddAssert("Check participant count correct", () => client.APIRoom?.ParticipantCount.Value == 1); AddAssert("Check participant list contains user", () => client.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); @@ -353,7 +353,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().First().TriggerClick()); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); - AddUntilStep("wait for join", () => roomManager.RoomJoined); + AddUntilStep("wait for join", () => client.RoomJoined); } [Test] @@ -646,7 +646,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("join room", () => InputManager.Key(Key.Enter)); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); - AddUntilStep("wait for join", () => roomManager.RoomJoined); + AddUntilStep("wait for join", () => client.RoomJoined); AddAssert("local room has correct settings", () => { @@ -838,7 +838,7 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - AddUntilStep("wait for join", () => roomManager.RoomJoined); + AddUntilStep("wait for join", () => client.RoomJoined); } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index ccce26ad31..4ecbc30d76 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -187,7 +187,7 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - AddUntilStep("wait for join", () => multiplayerScreenStack.RoomManager.RoomJoined); + AddUntilStep("wait for join", () => client.RoomJoined); } } } diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index 5656704abf..7607122ef0 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Multiplayer protected new MultiplayerTestSceneDependencies OnlinePlayDependencies => (MultiplayerTestSceneDependencies)base.OnlinePlayDependencies; - public bool RoomJoined => RoomManager.RoomJoined; + public bool RoomJoined => Client.RoomJoined; private readonly bool joinRoom; diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 4e0cfe405e..5b08b6b835 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -31,9 +31,10 @@ namespace osu.Game.Tests.Visual.Multiplayer private readonly Bindable isConnected = new Bindable(true); public new Room? APIRoom => base.APIRoom; - public Action? RoomSetupAction; + public bool RoomJoined { get; private set; } + [Resolved] private IAPIProvider api { get; set; } = null!; @@ -49,7 +50,6 @@ namespace osu.Game.Tests.Visual.Multiplayer private MultiplayerPlaylistItem? currentItem => Room?.Playlist[currentIndex]; private int currentIndex; - private long lastPlaylistItemId; public TestMultiplayerClient(TestMultiplayerRoomManager roomManager) @@ -217,9 +217,15 @@ namespace osu.Game.Tests.Visual.Multiplayer // emulate the server sending this after the join room. scheduler required to make sure the join room event is fired first (in Join). changeMatchType(Room.Settings.MatchType).Wait(); + + RoomJoined = true; } - protected override Task LeaveRoomInternal() => Task.CompletedTask; + protected override Task LeaveRoomInternal() + { + RoomJoined = false; + return Task.CompletedTask; + } public override Task TransferHost(int userId) => ((IMultiplayerClient)this).HostChanged(userId); diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index a1f010f082..296db3152d 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -17,8 +17,6 @@ namespace osu.Game.Tests.Visual.Multiplayer /// public class TestMultiplayerRoomManager : MultiplayerRoomManager { - public bool RoomJoined { get; private set; } - private readonly TestRoomRequestsHandler requestsHandler; public TestMultiplayerRoomManager(TestRoomRequestsHandler requestsHandler) @@ -29,28 +27,10 @@ namespace osu.Game.Tests.Visual.Multiplayer public IReadOnlyList ServerSideRooms => requestsHandler.ServerSideRooms; public override void CreateRoom(Room room, Action onSuccess = null, Action onError = null) - { - base.CreateRoom(room, r => - { - onSuccess?.Invoke(r); - RoomJoined = true; - }, onError); - } + => base.CreateRoom(room, r => onSuccess?.Invoke(r), onError); public override void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) - { - base.JoinRoom(room, password, r => - { - onSuccess?.Invoke(r); - RoomJoined = true; - }, onError); - } - - public override void PartRoom() - { - base.PartRoom(); - RoomJoined = false; - } + => base.JoinRoom(room, password, r => onSuccess?.Invoke(r), onError); /// /// Adds a room to a local "server-side" list that's returned when a is fired. From d79602a9127ffcff60597dcbb8291da70566ffe2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Dec 2021 16:58:16 +0900 Subject: [PATCH 22/61] Add wait step to `TestScenePlaylistResultScreen` explicitly for screen load --- .../Visual/Playlists/TestScenePlaylistsResultsScreen.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 25ca1299ef..e9210496ca 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -168,12 +168,13 @@ namespace osu.Game.Tests.Visual.Playlists })); }); + AddUntilStep("wait for screen to load", () => resultsScreen.IsLoaded); waitForDisplay(); } private void waitForDisplay() { - AddUntilStep("wait for load to complete", () => + AddUntilStep("wait for scores loaded", () => requestComplete && resultsScreen.ScorePanelList.GetScorePanels().Count() == totalCount && resultsScreen.ScorePanelList.AllPanelsVisible); From af78a3e99d9126d6bd47219b4fe6a1b6625376d4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Dec 2021 17:09:08 +0900 Subject: [PATCH 23/61] Fix weird loop logic --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index be390742ea..01692ebc80 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -883,7 +883,7 @@ namespace osu.Game.Tests.Visual.SongSelect { var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray(); - for (int i = 0; i < 100; i += 10) + for (int i = 0; i < 10; i++) manager.Import(TestResources.CreateTestBeatmapSetInfo(rulesets: usableRulesets)).Wait(); }); } From 5c8e317a6e7b647b40e1f92a9d0d6eb17dbafda5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Dec 2021 17:11:15 +0900 Subject: [PATCH 24/61] Chooser earlier items in song select tests to avoid potentially not having enough --- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 01692ebc80..dcc34d2eaa 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -488,8 +488,9 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select beatmap externally", () => { target = manager.GetAllUsableBeatmapSets() - .Where(b => b.Beatmaps.Any(bi => bi.RulesetID == targetRuleset)) - .ElementAt(5).Beatmaps.First(bi => bi.RulesetID == targetRuleset); + .First(b => b.Beatmaps.Any(bi => bi.RulesetID == targetRuleset)) + .Beatmaps + .First(bi => bi.RulesetID == targetRuleset); Beatmap.Value = manager.GetWorkingBeatmap(target); }); @@ -534,8 +535,10 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select beatmap externally", () => { - target = manager.GetAllUsableBeatmapSets().Where(b => b.Beatmaps.Any(bi => bi.RulesetID == 1)) - .ElementAt(5).Beatmaps.First(); + target = manager + .GetAllUsableBeatmapSets() + .First(b => b.Beatmaps.Any(bi => bi.RulesetID == 1)) + .Beatmaps.First(); Beatmap.Value = manager.GetWorkingBeatmap(target); }); From 6caa950c44aba65eb9aef5aa2895d79e6ee0ddda Mon Sep 17 00:00:00 2001 From: MBmasher Date: Mon, 20 Dec 2021 19:16:41 +1100 Subject: [PATCH 25/61] Rename `osuPrevious` to `osuLoop` --- .../Difficulty/Skills/Flashlight.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 9c539d5e4b..cb1ccf949e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -42,27 +42,27 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills for (int i = 0; i < Previous.Count; i++) { - var osuPrevious = (OsuDifficultyHitObject)Previous[i]; - var osuPreviousHitObject = (OsuHitObject)(osuPrevious.BaseObject); + var osuLoop = (OsuDifficultyHitObject)Previous[i]; + var osuLoopHitObject = (OsuHitObject)(osuLoop.BaseObject); - OsuDifficultyHitObject osuLastPrevious; + OsuDifficultyHitObject osuLoopNext; if (i == 0) - osuLastPrevious = osuCurrent; + osuLoopNext = osuCurrent; else - osuLastPrevious = (OsuDifficultyHitObject)Previous[i - 1]; + osuLoopNext = (OsuDifficultyHitObject)Previous[i - 1]; - if (!(osuPrevious.BaseObject is Spinner)) + if (!(osuLoop.BaseObject is Spinner)) { - double jumpDistance = (osuHitObject.StackedPosition - osuPreviousHitObject.EndPosition).Length; + double jumpDistance = (osuHitObject.StackedPosition - osuLoopHitObject.EndPosition).Length; - cumulativeStrainTime += osuLastPrevious.StrainTime; + cumulativeStrainTime += osuLoopNext.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. if (i == 0) smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); // We also want to nerf stacks so that only the first object of the stack is accounted for. - double stackNerf = Math.Min(1.0, (osuPrevious.JumpDistance / scalingFactor) / 25.0); + double stackNerf = Math.Min(1.0, (osuLoop.JumpDistance / scalingFactor) / 25.0); result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } From 090c3e84e7004e544f7c57374865c87ceac41109 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Dec 2021 17:38:14 +0900 Subject: [PATCH 26/61] Avoid blocking windows key usage when the osu! window is not active As discussed in https://github.com/ppy/osu/discussions/16147. --- osu.Desktop/Windows/GameplayWinKeyBlocker.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Desktop/Windows/GameplayWinKeyBlocker.cs b/osu.Desktop/Windows/GameplayWinKeyBlocker.cs index dbfd170ea1..4acaf61cea 100644 --- a/osu.Desktop/Windows/GameplayWinKeyBlocker.cs +++ b/osu.Desktop/Windows/GameplayWinKeyBlocker.cs @@ -14,6 +14,7 @@ namespace osu.Desktop.Windows { private Bindable disableWinKey; private IBindable localUserPlaying; + private IBindable isActive; [Resolved] private GameHost host { get; set; } @@ -24,13 +25,16 @@ namespace osu.Desktop.Windows localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy(); localUserPlaying.BindValueChanged(_ => updateBlocking()); + isActive = host.IsActive.GetBoundCopy(); + isActive.BindValueChanged(_ => updateBlocking()); + disableWinKey = config.GetBindable(OsuSetting.GameplayDisableWinKey); disableWinKey.BindValueChanged(_ => updateBlocking(), true); } private void updateBlocking() { - bool shouldDisable = disableWinKey.Value && localUserPlaying.Value; + bool shouldDisable = isActive.Value && disableWinKey.Value && localUserPlaying.Value; if (shouldDisable) host.InputThread.Scheduler.Add(WindowsKey.Disable); From 87051d5d61c71c8045c668686fe5b3020cb548a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Dec 2021 17:46:20 +0900 Subject: [PATCH 27/61] Add better defined steps to `TestLoseHostWhileReady` ready button test Not 100% sure this will solve the issue but it's worth a try. The button state checks are using `Until` everywhere else so this brings the test in line with the standards. As seen https://github.com/ppy/osu/runs/4579641456?check_suite_focus=true. --- .../Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 84b63a5733..81220e2527 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -163,10 +163,13 @@ namespace osu.Game.Tests.Visual.Multiplayer }); addClickButtonStep(); + AddUntilStep("user is ready", () => Client.Room?.Users[0].State == MultiplayerUserState.Ready); + AddStep("transfer host", () => Client.TransferHost(Client.Room?.Users[1].UserID ?? 0)); addClickButtonStep(); - AddAssert("match not started", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle); + AddUntilStep("user is idle (match not started)", () => Client.Room?.Users[0].State == MultiplayerUserState.Idle); + AddAssert("ready button enabled", () => button.ChildrenOfType().Single().Enabled.Value); } [TestCase(true)] From 1533aefce5015d03083501d5bb6547bb77e7b5c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Dec 2021 18:22:19 +0900 Subject: [PATCH 28/61] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 209b8cd63e..1532d4ce23 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 97bdf00f69..6e6002bc8e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index d4b7339900..de359245d1 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -60,7 +60,7 @@ - + @@ -83,7 +83,7 @@ - + From 6907a9a3ccb5ef46863b5eb4bd6231f678fc606c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Dec 2021 18:24:14 +0900 Subject: [PATCH 29/61] Name some screen stacks for better logging context --- osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs | 6 +++++- .../Screens/OnlinePlay/Multiplayer/Spectate/PlayerArea.cs | 5 ++++- osu.Game/Tests/Visual/ScreenTestScene.cs | 6 +++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs b/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs index 370f3bd0ae..88502b7074 100644 --- a/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs +++ b/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs @@ -49,7 +49,11 @@ namespace osu.Game.Tests.Visual InternalChildren = new Drawable[] { Client = new TestMultiplayerClient(RoomManager), - screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both } + screenStack = new OsuScreenStack + { + Name = nameof(TestMultiplayerScreenStack), + RelativeSizeAxes = Axes.Both + } }; screenStack.Push(multiplayerScreen); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerArea.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerArea.cs index c3190cd845..48f153ecbe 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerArea.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerArea.cs @@ -87,7 +87,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate gameplayContent.Child = new PlayerIsolationContainer(beatmapManager.GetWorkingBeatmap(Score.ScoreInfo.BeatmapInfo), Score.ScoreInfo.Ruleset, Score.ScoreInfo.Mods) { RelativeSizeAxes = Axes.Both, - Child = stack = new OsuScreenStack() + Child = stack = new OsuScreenStack + { + Name = nameof(PlayerArea), + } }; stack.Push(new MultiSpectatorPlayerLoader(Score, () => new MultiSpectatorPlayer(Score, GameplayClock))); diff --git a/osu.Game/Tests/Visual/ScreenTestScene.cs b/osu.Game/Tests/Visual/ScreenTestScene.cs index aa46b516bf..c44a848275 100644 --- a/osu.Game/Tests/Visual/ScreenTestScene.cs +++ b/osu.Game/Tests/Visual/ScreenTestScene.cs @@ -29,7 +29,11 @@ namespace osu.Game.Tests.Visual { base.Content.AddRange(new Drawable[] { - Stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, + Stack = new OsuScreenStack + { + Name = nameof(ScreenTestScene), + RelativeSizeAxes = Axes.Both + }, content = new Container { RelativeSizeAxes = Axes.Both }, DialogOverlay = new DialogOverlay() }); From ec0a6735eb519272f85413c0017e9dcf95ed11e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Dec 2021 18:24:59 +0900 Subject: [PATCH 30/61] Rename `TestMultiplayerScreenStack` to `TestMultiplayerComponents` --- .../Visual/Multiplayer/QueueModeTestScene.cs | 20 +++---- .../Multiplayer/TestSceneMultiplayer.cs | 52 +++++++++---------- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 18 +++---- .../Navigation/TestSceneScreenNavigation.cs | 8 +-- ...nStack.cs => TestMultiplayerComponents.cs} | 6 +-- 5 files changed, 52 insertions(+), 52 deletions(-) rename osu.Game.Tests/Visual/{TestMultiplayerScreenStack.cs => TestMultiplayerComponents.cs} (95%) diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index 83292c36bf..5acb44ac45 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs @@ -31,16 +31,16 @@ namespace osu.Game.Tests.Visual.Multiplayer protected BeatmapInfo InitialBeatmap { get; private set; } protected BeatmapInfo OtherBeatmap { get; private set; } - protected IScreen CurrentScreen => multiplayerScreenStack.CurrentScreen; - protected IScreen CurrentSubScreen => multiplayerScreenStack.MultiplayerScreen.CurrentSubScreen; + protected IScreen CurrentScreen => multiplayerComponents.CurrentScreen; + protected IScreen CurrentSubScreen => multiplayerComponents.MultiplayerScreen.CurrentSubScreen; private BeatmapManager beatmaps; private RulesetStore rulesets; private BeatmapSetInfo importedSet; - private TestMultiplayerScreenStack multiplayerScreenStack; + private TestMultiplayerComponents multiplayerComponents; - protected TestMultiplayerClient Client => multiplayerScreenStack.Client; + protected TestMultiplayerClient Client => multiplayerComponents.Client; [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); @@ -64,12 +64,12 @@ namespace osu.Game.Tests.Visual.Multiplayer OtherBeatmap = importedSet.Beatmaps.Last(b => b.RulesetID == 0); }); - AddStep("load multiplayer", () => LoadScreen(multiplayerScreenStack = new TestMultiplayerScreenStack())); - AddUntilStep("wait for multiplayer to load", () => multiplayerScreenStack.IsLoaded); + AddStep("load multiplayer", () => LoadScreen(multiplayerComponents = new TestMultiplayerComponents())); + AddUntilStep("wait for multiplayer to load", () => multiplayerComponents.IsLoaded); AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); - AddUntilStep("wait for lounge", () => multiplayerScreenStack.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); - AddStep("open room", () => multiplayerScreenStack.ChildrenOfType().Single().Open(new Room + AddUntilStep("wait for lounge", () => multiplayerComponents.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + AddStep("open room", () => multiplayerComponents.ChildrenOfType().Single().Open(new Room { Name = { Value = "Test Room" }, QueueMode = { Value = Mode }, @@ -109,8 +109,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for ready", () => Client.LocalUser?.State == MultiplayerUserState.Ready); clickReadyButton(); - AddUntilStep("wait for player", () => multiplayerScreenStack.CurrentScreen is Player player && player.IsLoaded); - AddStep("exit player", () => multiplayerScreenStack.MultiplayerScreen.MakeCurrent()); + AddUntilStep("wait for player", () => multiplayerComponents.CurrentScreen is Player player && player.IsLoaded); + AddStep("exit player", () => multiplayerComponents.MultiplayerScreen.MakeCurrent()); } private void clickReadyButton() diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 99411254a7..bd0e5c4eb9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -46,10 +46,10 @@ namespace osu.Game.Tests.Visual.Multiplayer private RulesetStore rulesets; private BeatmapSetInfo importedSet; - private TestMultiplayerScreenStack multiplayerScreenStack; + private TestMultiplayerComponents multiplayerComponents; - private TestMultiplayerClient client => multiplayerScreenStack.Client; - private TestMultiplayerRoomManager roomManager => multiplayerScreenStack.RoomManager; + private TestMultiplayerClient client => multiplayerComponents.Client; + private TestMultiplayerRoomManager roomManager => multiplayerComponents.RoomManager; [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); @@ -71,8 +71,8 @@ namespace osu.Game.Tests.Visual.Multiplayer importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); }); - AddStep("load multiplayer", () => LoadScreen(multiplayerScreenStack = new TestMultiplayerScreenStack())); - AddUntilStep("wait for multiplayer to load", () => multiplayerScreenStack.IsLoaded); + AddStep("load multiplayer", () => LoadScreen(multiplayerComponents = new TestMultiplayerComponents())); + AddUntilStep("wait for multiplayer to load", () => multiplayerComponents.IsLoaded); AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); } @@ -419,7 +419,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("Enter song select", () => { - var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerScreenStack.CurrentScreen).CurrentSubScreen; + var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen; ((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(client.Room?.Settings.PlaylistItemId); }); @@ -433,7 +433,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("start match externally", () => client.StartMatch()); - AddUntilStep("play started", () => multiplayerScreenStack.CurrentScreen is Player); + AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player); AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == client.Room?.Playlist.First().BeatmapID); } @@ -473,7 +473,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("start match externally", () => client.StartMatch()); - AddAssert("play not started", () => multiplayerScreenStack.IsCurrentScreen()); + AddAssert("play not started", () => multiplayerComponents.IsCurrentScreen()); } [Test] @@ -517,7 +517,7 @@ namespace osu.Game.Tests.Visual.Multiplayer importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); }); - AddUntilStep("play started", () => multiplayerScreenStack.CurrentScreen is SpectatorScreen); + AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is SpectatorScreen); } [Test] @@ -559,16 +559,16 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("open mod overlay", () => this.ChildrenOfType().Single().TriggerClick()); - AddStep("invoke on back button", () => multiplayerScreenStack.OnBackButton()); + AddStep("invoke on back button", () => multiplayerComponents.OnBackButton()); AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden); AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden); - testLeave("back button", () => multiplayerScreenStack.OnBackButton()); + testLeave("back button", () => multiplayerComponents.OnBackButton()); // mimics home button and OS window close - testLeave("forced exit", () => multiplayerScreenStack.Exit()); + testLeave("forced exit", () => multiplayerComponents.Exit()); void testLeave(string actionName, Action action) { @@ -605,7 +605,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep($"wait for time > {i}", () => this.ChildrenOfType().SingleOrDefault()?.GameplayClock.CurrentTime > time); } - AddUntilStep("wait for results", () => multiplayerScreenStack.CurrentScreen is ResultsScreen); + AddUntilStep("wait for results", () => multiplayerComponents.CurrentScreen is ResultsScreen); } [Test] @@ -680,15 +680,15 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("set other user ready", () => client.ChangeUserState(1234, MultiplayerUserState.Ready)); pressReadyButton(1234); - AddUntilStep("wait for gameplay", () => (multiplayerScreenStack.CurrentScreen as MultiSpectatorScreen)?.IsLoaded == true); + AddUntilStep("wait for gameplay", () => (multiplayerComponents.CurrentScreen as MultiSpectatorScreen)?.IsLoaded == true); AddStep("press back button and exit", () => { - multiplayerScreenStack.OnBackButton(); - multiplayerScreenStack.Exit(); + multiplayerComponents.OnBackButton(); + multiplayerComponents.Exit(); }); - AddUntilStep("wait for return to match subscreen", () => multiplayerScreenStack.MultiplayerScreen.IsCurrentScreen()); + AddUntilStep("wait for return to match subscreen", () => multiplayerComponents.MultiplayerScreen.IsCurrentScreen()); AddUntilStep("user state is idle", () => client.LocalUser?.State == MultiplayerUserState.Idle); } @@ -716,17 +716,17 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("set other user ready", () => client.ChangeUserState(1234, MultiplayerUserState.Ready)); pressReadyButton(1234); - AddUntilStep("wait for gameplay", () => (multiplayerScreenStack.CurrentScreen as MultiSpectatorScreen)?.IsLoaded == true); + AddUntilStep("wait for gameplay", () => (multiplayerComponents.CurrentScreen as MultiSpectatorScreen)?.IsLoaded == true); AddStep("set other user loaded", () => client.ChangeUserState(1234, MultiplayerUserState.Loaded)); AddStep("set other user finished play", () => client.ChangeUserState(1234, MultiplayerUserState.FinishedPlay)); AddStep("press back button and exit", () => { - multiplayerScreenStack.OnBackButton(); - multiplayerScreenStack.Exit(); + multiplayerComponents.OnBackButton(); + multiplayerComponents.Exit(); }); - AddUntilStep("wait for return to match subscreen", () => multiplayerScreenStack.MultiplayerScreen.IsCurrentScreen()); + AddUntilStep("wait for return to match subscreen", () => multiplayerComponents.MultiplayerScreen.IsCurrentScreen()); AddWaitStep("wait for possible state change", 5); AddUntilStep("user state is spectating", () => client.LocalUser?.State == MultiplayerUserState.Spectating); } @@ -758,7 +758,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("item arrived in playlist", () => client.Room?.Playlist.Count == 2); - AddStep("exit gameplay as initial user", () => multiplayerScreenStack.MultiplayerScreen.MakeCurrent()); + AddStep("exit gameplay as initial user", () => multiplayerComponents.MultiplayerScreen.MakeCurrent()); AddUntilStep("queue contains item", () => this.ChildrenOfType().Single().Items.Single().ID == 2); } @@ -792,7 +792,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("delete item as other user", () => client.RemoveUserPlaylistItem(1234, 2)); AddUntilStep("item removed from playlist", () => client.Room?.Playlist.Count == 1); - AddStep("exit gameplay as initial user", () => multiplayerScreenStack.MultiplayerScreen.MakeCurrent()); + AddStep("exit gameplay as initial user", () => multiplayerComponents.MultiplayerScreen.MakeCurrent()); AddUntilStep("queue is empty", () => this.ChildrenOfType().Single().Items.Count == 0); } @@ -800,7 +800,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { pressReadyButton(); pressReadyButton(); - AddUntilStep("wait for player", () => multiplayerScreenStack.CurrentScreen is Player); + AddUntilStep("wait for player", () => multiplayerComponents.CurrentScreen is Player); } private ReadyButton readyButton => this.ChildrenOfType().Single(); @@ -826,8 +826,8 @@ namespace osu.Game.Tests.Visual.Multiplayer private void createRoom(Func room) { - AddUntilStep("wait for lounge", () => multiplayerScreenStack.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); - AddStep("open room", () => multiplayerScreenStack.ChildrenOfType().Single().Open(room())); + AddUntilStep("wait for lounge", () => multiplayerComponents.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + AddStep("open room", () => multiplayerComponents.ChildrenOfType().Single().Open(room())); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddWaitStep("wait for transition", 2); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 4ecbc30d76..81c59b90f5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -31,9 +31,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private RulesetStore rulesets; private BeatmapSetInfo importedSet; - private TestMultiplayerScreenStack multiplayerScreenStack; + private TestMultiplayerComponents multiplayerComponents; - private TestMultiplayerClient client => multiplayerScreenStack.Client; + private TestMultiplayerClient client => multiplayerComponents.Client; [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); @@ -55,8 +55,8 @@ namespace osu.Game.Tests.Visual.Multiplayer importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); }); - AddStep("load multiplayer", () => LoadScreen(multiplayerScreenStack = new TestMultiplayerScreenStack())); - AddUntilStep("wait for multiplayer to load", () => multiplayerScreenStack.IsLoaded); + AddStep("load multiplayer", () => LoadScreen(multiplayerComponents = new TestMultiplayerComponents())); + AddUntilStep("wait for multiplayer to load", () => multiplayerComponents.IsLoaded); AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); } @@ -103,7 +103,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("press own button", () => { - InputManager.MoveMouseTo(multiplayerScreenStack.ChildrenOfType().First()); + InputManager.MoveMouseTo(multiplayerComponents.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); AddAssert("user on team 1", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 1); @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("press other user's button", () => { - InputManager.MoveMouseTo(multiplayerScreenStack.ChildrenOfType().ElementAt(1)); + InputManager.MoveMouseTo(multiplayerComponents.ChildrenOfType().ElementAt(1)); InputManager.Click(MouseButton.Left); }); AddAssert("user still on team 0", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0); @@ -164,18 +164,18 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("room type is head to head", () => client.Room?.Settings.MatchType == MatchType.HeadToHead); - AddUntilStep("team displays are not displaying teams", () => multiplayerScreenStack.ChildrenOfType().All(d => d.DisplayedTeam == null)); + AddUntilStep("team displays are not displaying teams", () => multiplayerComponents.ChildrenOfType().All(d => d.DisplayedTeam == null)); AddStep("change to team vs", () => client.ChangeSettings(matchType: MatchType.TeamVersus)); AddUntilStep("room type is team vs", () => client.Room?.Settings.MatchType == MatchType.TeamVersus); - AddUntilStep("team displays are displaying teams", () => multiplayerScreenStack.ChildrenOfType().All(d => d.DisplayedTeam != null)); + AddUntilStep("team displays are displaying teams", () => multiplayerComponents.ChildrenOfType().All(d => d.DisplayedTeam != null)); } private void createRoom(Func room) { - AddStep("open room", () => multiplayerScreenStack.ChildrenOfType().Single().Open(room())); + AddStep("open room", () => multiplayerComponents.ChildrenOfType().Single().Open(room())); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddWaitStep("wait for transition", 2); diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 664c186cf8..48ab643992 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -336,12 +336,12 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestPushMatchSubScreenAndPressBackButtonImmediately() { - TestMultiplayerScreenStack multiplayerScreenStack = null; + TestMultiplayerComponents multiplayerComponents = null; - PushAndConfirm(() => multiplayerScreenStack = new TestMultiplayerScreenStack()); + PushAndConfirm(() => multiplayerComponents = new TestMultiplayerComponents()); - AddUntilStep("wait for lounge", () => multiplayerScreenStack.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); - AddStep("open room", () => multiplayerScreenStack.ChildrenOfType().Single().Open()); + AddUntilStep("wait for lounge", () => multiplayerComponents.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + AddStep("open room", () => multiplayerComponents.ChildrenOfType().Single().Open()); AddStep("press back button", () => Game.ChildrenOfType().First().Action()); AddWaitStep("wait two frames", 2); } diff --git a/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs similarity index 95% rename from osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs rename to osu.Game.Tests/Visual/TestMultiplayerComponents.cs index 88502b7074..cd7a936778 100644 --- a/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs +++ b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual /// ///

///
- public class TestMultiplayerScreenStack : OsuScreen + public class TestMultiplayerComponents : OsuScreen { public Screens.OnlinePlay.Multiplayer.Multiplayer MultiplayerScreen => multiplayerScreen; @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual private readonly OsuScreenStack screenStack; private readonly TestMultiplayer multiplayerScreen; - public TestMultiplayerScreenStack() + public TestMultiplayerComponents() { multiplayerScreen = new TestMultiplayer(); @@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual Client = new TestMultiplayerClient(RoomManager), screenStack = new OsuScreenStack { - Name = nameof(TestMultiplayerScreenStack), + Name = nameof(TestMultiplayerComponents), RelativeSizeAxes = Axes.Both } }; From f492cf84d9d83d8ea9a39e5c1c511471d5bd7f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Dec 2021 11:24:40 +0100 Subject: [PATCH 31/61] Ensure presence of at least 1 difficulty for each ruleset --- .../SongSelect/TestScenePlaySongSelect.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index dcc34d2eaa..912d3f838c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -466,7 +466,9 @@ namespace osu.Game.Tests.Visual.SongSelect public void TestExternalBeatmapChangeWhileFiltered(bool differentRuleset) { createSongSelect(); - addManyTestMaps(); + // ensure there is at least 1 difficulty for each of the rulesets + // (catch is excluded inside of addManyTestMaps). + addManyTestMaps(3); changeRuleset(0); @@ -519,7 +521,9 @@ namespace osu.Game.Tests.Visual.SongSelect public void TestExternalBeatmapChangeWhileFilteredThenRefilter() { createSongSelect(); - addManyTestMaps(); + // ensure there is at least 1 difficulty for each of the rulesets + // (catch is excluded inside of addManyTestMaps). + addManyTestMaps(3); changeRuleset(0); @@ -880,14 +884,21 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("wait for carousel loaded", () => songSelect.Carousel.IsAlive); } - private void addManyTestMaps() + /// + /// Imports test beatmap sets to show in the carousel. + /// + /// + /// The exact count of difficulties to create for each beatmap set. + /// A value causes the count of difficulties to be selected randomly. + /// + private void addManyTestMaps(int? difficultyCountPerSet = null) { AddStep("import test maps", () => { var usableRulesets = rulesets.AvailableRulesets.Where(r => r.OnlineID != 2).ToArray(); for (int i = 0; i < 10; i++) - manager.Import(TestResources.CreateTestBeatmapSetInfo(rulesets: usableRulesets)).Wait(); + manager.Import(TestResources.CreateTestBeatmapSetInfo(difficultyCountPerSet, usableRulesets)).Wait(); }); } From a59583ee0901c3086059cd38b44ffcb86c7729b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Dec 2021 13:18:02 +0100 Subject: [PATCH 32/61] Add extension method for returning next playlist item --- .../OnlinePlay/PlaylistExtensionsTest.cs | 72 +++++++++++++++++++ osu.Game/Online/Rooms/PlaylistExtensions.cs | 10 +++ 2 files changed, 82 insertions(+) create mode 100644 osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs diff --git a/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs b/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs new file mode 100644 index 0000000000..d2ee47bc23 --- /dev/null +++ b/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs @@ -0,0 +1,72 @@ +// 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.Online.Rooms; + +namespace osu.Game.Tests.OnlinePlay +{ + [TestFixture] + public class PlaylistExtensionsTest + { + [Test] + public void TestPlaylistItemsInOrder() + { + var items = new[] + { + new PlaylistItem { ID = 1, BeatmapID = 1001, PlaylistOrder = 1 }, + new PlaylistItem { ID = 2, BeatmapID = 1002, PlaylistOrder = 2 }, + new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, + }; + + var nextItem = items.GetNextItem(); + + Assert.That(nextItem, Is.EqualTo(items[0])); + } + + [Test] + public void TestPlaylistItemsOutOfOrder() + { + var items = new[] + { + new PlaylistItem { ID = 2, BeatmapID = 1002, PlaylistOrder = 2 }, + new PlaylistItem { ID = 1, BeatmapID = 1001, PlaylistOrder = 1 }, + new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, + }; + + var nextItem = items.GetNextItem(); + + Assert.That(nextItem, Is.EqualTo(items[1])); + } + + [Test] + public void TestExpiredPlaylistItemsSkipped() + { + var items = new[] + { + new PlaylistItem { ID = 2, BeatmapID = 1002, PlaylistOrder = 2, Expired = true }, + new PlaylistItem { ID = 1, BeatmapID = 1001, PlaylistOrder = 1, Expired = true }, + new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, + }; + + var nextItem = items.GetNextItem(); + + Assert.That(nextItem, Is.EqualTo(items[2])); + } + + [Test] + public void TestAllItemsExpired() + { + var items = new[] + { + new PlaylistItem { ID = 2, BeatmapID = 1002, PlaylistOrder = 2, Expired = true }, + new PlaylistItem { ID = 1, BeatmapID = 1001, PlaylistOrder = 1, Expired = true }, + new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3, Expired = true }, + }; + + var nextItem = items.GetNextItem(); + + Assert.That(nextItem, Is.Null); + } + } +} diff --git a/osu.Game/Online/Rooms/PlaylistExtensions.cs b/osu.Game/Online/Rooms/PlaylistExtensions.cs index 992011da3c..e900dc53ef 100644 --- a/osu.Game/Online/Rooms/PlaylistExtensions.cs +++ b/osu.Game/Online/Rooms/PlaylistExtensions.cs @@ -1,6 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + +using System.Collections.Generic; using System.Linq; using Humanizer; using Humanizer.Localisation; @@ -10,6 +13,13 @@ namespace osu.Game.Online.Rooms { public static class PlaylistExtensions { + /// + /// Returns the next to be played from the supplied , + /// or if all items are expired. + /// + public static PlaylistItem? GetNextItem(this IEnumerable playlist) => + playlist.OrderBy(item => item.PlaylistOrder).FirstOrDefault(item => !item.Expired); + public static string GetTotalDuration(this BindableList playlist) => playlist.Select(p => p.Beatmap.Value.Length).Sum().Milliseconds().Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 2); } From a5a9922f81529f46d2efe46e11af0c6822512bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Dec 2021 13:19:00 +0100 Subject: [PATCH 33/61] Fix lounge screen content not matching current room playlist item --- osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs | 8 ++++---- osu.Game/Online/Rooms/PlaylistExtensions.cs | 4 ++-- .../OnlinePlay/Components/OnlinePlayBackgroundSprite.cs | 4 ++-- .../Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs | 3 +-- osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs | 5 ++--- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs b/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs index d2ee47bc23..001513be1f 100644 --- a/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs +++ b/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.OnlinePlay new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, }; - var nextItem = items.GetNextItem(); + var nextItem = items.GetCurrentItem(); Assert.That(nextItem, Is.EqualTo(items[0])); } @@ -34,7 +34,7 @@ namespace osu.Game.Tests.OnlinePlay new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, }; - var nextItem = items.GetNextItem(); + var nextItem = items.GetCurrentItem(); Assert.That(nextItem, Is.EqualTo(items[1])); } @@ -49,7 +49,7 @@ namespace osu.Game.Tests.OnlinePlay new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, }; - var nextItem = items.GetNextItem(); + var nextItem = items.GetCurrentItem(); Assert.That(nextItem, Is.EqualTo(items[2])); } @@ -64,7 +64,7 @@ namespace osu.Game.Tests.OnlinePlay new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3, Expired = true }, }; - var nextItem = items.GetNextItem(); + var nextItem = items.GetCurrentItem(); Assert.That(nextItem, Is.Null); } diff --git a/osu.Game/Online/Rooms/PlaylistExtensions.cs b/osu.Game/Online/Rooms/PlaylistExtensions.cs index e900dc53ef..46cb218910 100644 --- a/osu.Game/Online/Rooms/PlaylistExtensions.cs +++ b/osu.Game/Online/Rooms/PlaylistExtensions.cs @@ -14,10 +14,10 @@ namespace osu.Game.Online.Rooms public static class PlaylistExtensions { /// - /// Returns the next to be played from the supplied , + /// Returns the first non-expired in playlist order from the supplied , /// or if all items are expired. /// - public static PlaylistItem? GetNextItem(this IEnumerable playlist) => + public static PlaylistItem? GetCurrentItem(this IEnumerable playlist) => playlist.OrderBy(item => item.PlaylistOrder).FirstOrDefault(item => !item.Expired); public static string GetTotalDuration(this BindableList playlist) => diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs index e2ba0b03b0..d144e1e3a9 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs @@ -1,10 +1,10 @@ // 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.Graphics; using osu.Game.Beatmaps.Drawables; +using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Components { @@ -30,7 +30,7 @@ namespace osu.Game.Screens.OnlinePlay.Components private void updateBeatmap() { - sprite.Beatmap.Value = Playlist.FirstOrDefault()?.Beatmap.Value; + sprite.Beatmap.Value = Playlist.GetCurrentItem()?.Beatmap.Value; } protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both }; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs index 6c00ca2e81..926c35c5da 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeBackgroundScreen.cs @@ -3,7 +3,6 @@ #nullable enable -using System.Linq; using osu.Framework.Bindables; using osu.Framework.Screens; using osu.Game.Online.Rooms; @@ -20,7 +19,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge public LoungeBackgroundScreen() { SelectedRoom.BindValueChanged(onSelectedRoomChanged); - playlist.BindCollectionChanged((_, __) => PlaylistItem = playlist.FirstOrDefault()); + playlist.BindCollectionChanged((_, __) => PlaylistItem = playlist.GetCurrentItem()); } private void onSelectedRoomChanged(ValueChangedEvent room) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs index 6b111d76a5..c833621fbc 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayComposite.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; @@ -73,7 +72,7 @@ namespace osu.Game.Screens.OnlinePlay private IBindable subScreenSelectedItem { get; set; } /// - /// The currently selected item in the , or the last item from + /// The currently selected item in the , or the current item from /// if this is not within a . /// protected readonly Bindable SelectedItem = new Bindable(); @@ -88,7 +87,7 @@ namespace osu.Game.Screens.OnlinePlay protected virtual void UpdateSelectedItem() => SelectedItem.Value = RoomID.Value == null || subScreenSelectedItem == null - ? Playlist.LastOrDefault() + ? Playlist.GetCurrentItem() : subScreenSelectedItem.Value; } } From 0975f570ba2bdf82dd4aee1bd36412b94cb0b31e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Dec 2021 13:42:49 +0100 Subject: [PATCH 34/61] Return last playlist item if all expired --- .../OnlinePlay/PlaylistExtensionsTest.cs | 29 ++++++++++++++----- osu.Game/Online/Rooms/PlaylistExtensions.cs | 14 +++++++-- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs b/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs index 001513be1f..5913cb0f29 100644 --- a/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs +++ b/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using NUnit.Framework; using osu.Game.Online.Rooms; @@ -9,6 +10,17 @@ namespace osu.Game.Tests.OnlinePlay [TestFixture] public class PlaylistExtensionsTest { + [Test] + public void TestEmpty() + { + // mostly an extreme edge case, i.e. during room creation. + var items = Array.Empty(); + + var currentItem = items.GetCurrentItem(); + + Assert.That(currentItem, Is.Null); + } + [Test] public void TestPlaylistItemsInOrder() { @@ -19,9 +31,9 @@ namespace osu.Game.Tests.OnlinePlay new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, }; - var nextItem = items.GetCurrentItem(); + var currentItem = items.GetCurrentItem(); - Assert.That(nextItem, Is.EqualTo(items[0])); + Assert.That(currentItem, Is.EqualTo(items[0])); } [Test] @@ -34,9 +46,9 @@ namespace osu.Game.Tests.OnlinePlay new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, }; - var nextItem = items.GetCurrentItem(); + var currentItem = items.GetCurrentItem(); - Assert.That(nextItem, Is.EqualTo(items[1])); + Assert.That(currentItem, Is.EqualTo(items[1])); } [Test] @@ -49,9 +61,9 @@ namespace osu.Game.Tests.OnlinePlay new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, }; - var nextItem = items.GetCurrentItem(); + var currentItem = items.GetCurrentItem(); - Assert.That(nextItem, Is.EqualTo(items[2])); + Assert.That(currentItem, Is.EqualTo(items[2])); } [Test] @@ -64,9 +76,10 @@ namespace osu.Game.Tests.OnlinePlay new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3, Expired = true }, }; - var nextItem = items.GetCurrentItem(); + var currentItem = items.GetCurrentItem(); - Assert.That(nextItem, Is.Null); + // if all items are expired, the last-played item is expected to be returned. + Assert.That(currentItem, Is.EqualTo(items[2])); } } } diff --git a/osu.Game/Online/Rooms/PlaylistExtensions.cs b/osu.Game/Online/Rooms/PlaylistExtensions.cs index 46cb218910..355ae6188d 100644 --- a/osu.Game/Online/Rooms/PlaylistExtensions.cs +++ b/osu.Game/Online/Rooms/PlaylistExtensions.cs @@ -15,10 +15,18 @@ namespace osu.Game.Online.Rooms { /// /// Returns the first non-expired in playlist order from the supplied , - /// or if all items are expired. + /// or the last-played if all items are expired, + /// or if was empty. /// - public static PlaylistItem? GetCurrentItem(this IEnumerable playlist) => - playlist.OrderBy(item => item.PlaylistOrder).FirstOrDefault(item => !item.Expired); + public static PlaylistItem? GetCurrentItem(this ICollection playlist) + { + if (playlist.Count == 0) + return null; + + return playlist.All(item => item.Expired) + ? playlist.OrderByDescending(item => item.PlaylistOrder).First() + : playlist.OrderBy(item => item.PlaylistOrder).First(item => !item.Expired); + } public static string GetTotalDuration(this BindableList playlist) => playlist.Select(p => p.Beatmap.Value.Length).Sum().Milliseconds().Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 2); From 9344897542ff6b6463d8377d61f63b3ec0c8b5b3 Mon Sep 17 00:00:00 2001 From: dekrain Date: Mon, 20 Dec 2021 16:51:51 +0100 Subject: [PATCH 35/61] Split session statics reset method to prevent unloading seasonal backgrounds while idle --- osu.Game/Configuration/SessionStatics.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index ac94c39bd2..99f1e5a8b2 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -13,9 +13,7 @@ namespace osu.Game.Configuration /// public class SessionStatics : InMemoryConfigManager { - protected override void InitialiseDefaults() => ResetValues(); - - public void ResetValues() + protected override void InitialiseDefaults() { ensureDefault(SetDefault(Static.LoginOverlayDisplayed, false)); ensureDefault(SetDefault(Static.MutedAudioNotificationShownOnce, false)); @@ -24,6 +22,18 @@ namespace osu.Game.Configuration ensureDefault(SetDefault(Static.SeasonalBackgrounds, null)); } + /// + /// Used to revert statics to their defaults after being idle for appropiate amount of time. + /// Does not affect loaded seasonal backgrounds. + /// + public void ResetValues() + { + ensureDefault(GetBindable(Static.LoginOverlayDisplayed)); + ensureDefault(GetBindable(Static.MutedAudioNotificationShownOnce)); + ensureDefault(GetBindable(Static.LowBatteryNotificationShownOnce)); + ensureDefault(GetBindable(Static.LastHoverSoundPlaybackTime)); + } + private void ensureDefault(Bindable bindable) => bindable.SetDefault(); } From e21dbf10ff7734be7b0493d488f311e1a1657f1b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 21 Dec 2021 12:25:32 +0900 Subject: [PATCH 36/61] Refactor further to remove indexing confusion --- .../Difficulty/Skills/Flashlight.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index 21a2fc2252..ad7464244e 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -40,32 +40,33 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double result = 0.0; + OsuDifficultyHitObject lastObj = null; + + // This is iterating backwards in time from the current object. for (int i = 0; i < Previous.Count; i++) { - var osuLoop = (OsuDifficultyHitObject)Previous[i]; - var osuLoopHitObject = (OsuHitObject)(osuLoop.BaseObject); + var currentObj = (OsuDifficultyHitObject)Previous[i]; + var currentHitObject = (OsuHitObject)(currentObj.BaseObject); - OsuDifficultyHitObject osuLoopNext; - if (i == 0) - osuLoopNext = osuCurrent; - else - osuLoopNext = (OsuDifficultyHitObject)Previous[i - 1]; + lastObj ??= currentObj; - if (!(osuLoop.BaseObject is Spinner)) + if (!(currentObj.BaseObject is Spinner)) { - double jumpDistance = (osuHitObject.StackedPosition - osuLoopHitObject.EndPosition).Length; + double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.EndPosition).Length; - cumulativeStrainTime += osuLoopNext.StrainTime; + cumulativeStrainTime += lastObj.StrainTime; // We want to nerf objects that can be easily seen within the Flashlight circle radius. if (i == 0) smallDistNerf = Math.Min(1.0, jumpDistance / 75.0); // We also want to nerf stacks so that only the first object of the stack is accounted for. - double stackNerf = Math.Min(1.0, (osuLoop.LazyJumpDistance / scalingFactor) / 25.0); + double stackNerf = Math.Min(1.0, (currentObj.LazyJumpDistance / scalingFactor) / 25.0); result += Math.Pow(0.8, i) * stackNerf * scalingFactor * jumpDistance / cumulativeStrainTime; } + + lastObj = currentObj; } return Math.Pow(smallDistNerf * result, 2.0); From 28d6ff5d9c8bac9390e7576829623798934fc61a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Dec 2021 12:36:01 +0900 Subject: [PATCH 37/61] Fix potential wrong thread mutation in `ColourHitErrorMeter` --- .../Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index 5012be7249..00e6059541 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -53,13 +53,13 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters LayoutEasing = Easing.OutQuint; } - public void Push(Color4 colour) + public void Push(Color4 colour) => Schedule(() => { Add(new HitErrorCircle(colour, drawable_judgement_size)); if (Children.Count > max_available_judgements) Children.FirstOrDefault(c => !c.IsRemoved)?.Remove(); - } + }); } internal class HitErrorCircle : Container From c21b2d166223d2305a0f3d7ce8d9fc77c3bc4a66 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 21 Dec 2021 12:39:07 +0900 Subject: [PATCH 38/61] Fix incorrect variable --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index ad7464244e..bdf7032521 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills var currentObj = (OsuDifficultyHitObject)Previous[i]; var currentHitObject = (OsuHitObject)(currentObj.BaseObject); - lastObj ??= currentObj; + lastObj ??= osuCurrent; if (!(currentObj.BaseObject is Spinner)) { From f366cdc73eabbaff9224e3c7010e3f641a079158 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 21 Dec 2021 12:39:34 +0900 Subject: [PATCH 39/61] Extract initial set out of loop --- osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs index bdf7032521..ff0780be85 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Flashlight.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double result = 0.0; - OsuDifficultyHitObject lastObj = null; + OsuDifficultyHitObject lastObj = osuCurrent; // This is iterating backwards in time from the current object. for (int i = 0; i < Previous.Count; i++) @@ -48,8 +48,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills var currentObj = (OsuDifficultyHitObject)Previous[i]; var currentHitObject = (OsuHitObject)(currentObj.BaseObject); - lastObj ??= osuCurrent; - if (!(currentObj.BaseObject is Spinner)) { double jumpDistance = (osuHitObject.StackedPosition - currentHitObject.EndPosition).Length; From 52db7b36fc44a66d64a5b68d5a266ece48a2cebc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Dec 2021 12:55:21 +0900 Subject: [PATCH 40/61] Move `Schedule` call to base implementation of error meter for extra safety --- .../Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs | 4 ++-- osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index 00e6059541..5012be7249 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -53,13 +53,13 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters LayoutEasing = Easing.OutQuint; } - public void Push(Color4 colour) => Schedule(() => + public void Push(Color4 colour) { Add(new HitErrorCircle(colour, drawable_judgement_size)); if (Children.Count > max_available_judgements) Children.FirstOrDefault(c => !c.IsRemoved)?.Remove(); - }); + } } internal class HitErrorCircle : Container diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index c7b06a3a2c..16c23f07f5 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -40,9 +40,14 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters if (gameplayClockContainer != null) gameplayClockContainer.OnSeek += Clear; - processor.NewJudgement += OnNewJudgement; + // Scheduled as meter implementations are likely going to change/add drawables when reacting to this. + processor.NewJudgement += j => Schedule(() => OnNewJudgement(j)); } + /// + /// Fired when a new judgement arrives. + /// + /// The new judgement. protected abstract void OnNewJudgement(JudgementResult judgement); protected Color4 GetColourForHitResult(HitResult result) From edcbd4de6d27cd9788523064207e4111d2d99f80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Dec 2021 13:05:26 +0900 Subject: [PATCH 41/61] Fix incorrect event unbind logic --- osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs index 16c23f07f5..1f08cb8aa7 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/HitErrorMeter.cs @@ -40,10 +40,12 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters if (gameplayClockContainer != null) gameplayClockContainer.OnSeek += Clear; - // Scheduled as meter implementations are likely going to change/add drawables when reacting to this. - processor.NewJudgement += j => Schedule(() => OnNewJudgement(j)); + processor.NewJudgement += processorNewJudgement; } + // Scheduled as meter implementations are likely going to change/add drawables when reacting to this. + private void processorNewJudgement(JudgementResult j) => Schedule(() => OnNewJudgement(j)); + /// /// Fired when a new judgement arrives. /// @@ -89,7 +91,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters base.Dispose(isDisposing); if (processor != null) - processor.NewJudgement -= OnNewJudgement; + processor.NewJudgement -= processorNewJudgement; if (gameplayClockContainer != null) gameplayClockContainer.OnSeek -= Clear; From 9aff646ff49ac0194b194621b9c90e1af9ab2cc9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Dec 2021 13:20:12 +0900 Subject: [PATCH 42/61] Centralise all multiplayer button clicking test logic This adds the "wait for enabled" check in a way that can be easily reused, as it keeps getting missed in test implementations. This particular commit hopefully fixes https://github.com/ppy/osu/runs/4583845033?check_suite_focus=true. --- .../Visual/Multiplayer/QueueModeTestScene.cs | 23 ++----------- .../Multiplayer/TestSceneMultiplayer.cs | 18 ++-------- .../TestSceneMultiplayerMatchSubScreen.cs | 33 ++++--------------- .../TestSceneMultiplayerReadyButton.cs | 28 +++++----------- .../TestSceneMultiplayerSpectateButton.cs | 17 +++------- .../Visual/OsuManualInputManagerTestScene.cs | 23 +++++++++++++ 6 files changed, 48 insertions(+), 94 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index 5acb44ac45..88c54eb2bb 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs @@ -5,7 +5,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Graphics.UserInterface; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; @@ -20,7 +19,6 @@ using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.Play; using osu.Game.Tests.Resources; -using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { @@ -86,11 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddWaitStep("wait for transition", 2); - AddStep("create room", () => - { - InputManager.MoveMouseTo(this.ChildrenOfType().Single()); - InputManager.Click(MouseButton.Left); - }); + ClickButtonWhenEnabled(); AddUntilStep("wait for join", () => Client.RoomJoined); } @@ -104,24 +98,13 @@ namespace osu.Game.Tests.Visual.Multiplayer protected void RunGameplay() { AddUntilStep("wait for idle", () => Client.LocalUser?.State == MultiplayerUserState.Idle); - clickReadyButton(); + ClickButtonWhenEnabled(); AddUntilStep("wait for ready", () => Client.LocalUser?.State == MultiplayerUserState.Ready); - clickReadyButton(); + ClickButtonWhenEnabled(); AddUntilStep("wait for player", () => multiplayerComponents.CurrentScreen is Player player && player.IsLoaded); AddStep("exit player", () => multiplayerComponents.MultiplayerScreen.MakeCurrent()); } - - private void clickReadyButton() - { - AddUntilStep("wait for ready button to be enabled", () => this.ChildrenOfType().Single().ChildrenOfType