diff --git a/osu.Game.Tests/NonVisual/SessionStaticsTest.cs b/osu.Game.Tests/NonVisual/SessionStaticsTest.cs index d5fd803986..cd02f15adf 100644 --- a/osu.Game.Tests/NonVisual/SessionStaticsTest.cs +++ b/osu.Game.Tests/NonVisual/SessionStaticsTest.cs @@ -1,9 +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; using NUnit.Framework; using osu.Game.Configuration; -using osu.Game.Input; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Tests.NonVisual { @@ -11,37 +12,32 @@ namespace osu.Game.Tests.NonVisual public class SessionStaticsTest { private SessionStatics sessionStatics; - private IdleTracker sessionIdleTracker; - [SetUp] - public void SetUp() + [Test] + public void TestSessionStaticsReset() { sessionStatics = new SessionStatics(); - sessionIdleTracker = new GameIdleTracker(1000); sessionStatics.SetValue(Static.LoginOverlayDisplayed, true); sessionStatics.SetValue(Static.MutedAudioNotificationShownOnce, true); sessionStatics.SetValue(Static.LowBatteryNotificationShownOnce, true); sessionStatics.SetValue(Static.LastHoverSoundPlaybackTime, (double?)1d); + sessionStatics.SetValue(Static.SeasonalBackgrounds, new APISeasonalBackgrounds { EndDate = new DateTimeOffset(2022, 1, 1, 0, 0, 0, TimeSpan.Zero) }); - sessionIdleTracker.IsIdle.BindValueChanged(e => - { - if (e.NewValue) - sessionStatics.ResetValues(); - }); - } + Assert.IsFalse(sessionStatics.GetBindable(Static.LoginOverlayDisplayed).IsDefault); + Assert.IsFalse(sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce).IsDefault); + Assert.IsFalse(sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).IsDefault); + Assert.IsFalse(sessionStatics.GetBindable(Static.LastHoverSoundPlaybackTime).IsDefault); + Assert.IsFalse(sessionStatics.GetBindable(Static.SeasonalBackgrounds).IsDefault); - [Test] - [Timeout(2000)] - public void TestSessionStaticsReset() - { - sessionIdleTracker.IsIdle.BindValueChanged(e => - { - Assert.IsTrue(sessionStatics.GetBindable(Static.LoginOverlayDisplayed).IsDefault); - Assert.IsTrue(sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce).IsDefault); - Assert.IsTrue(sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).IsDefault); - Assert.IsTrue(sessionStatics.GetBindable(Static.LastHoverSoundPlaybackTime).IsDefault); - }); + sessionStatics.ResetAfterInactivity(); + + Assert.IsTrue(sessionStatics.GetBindable(Static.LoginOverlayDisplayed).IsDefault); + Assert.IsTrue(sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce).IsDefault); + Assert.IsTrue(sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).IsDefault); + // some statics should not reset despite inactivity. + Assert.IsFalse(sessionStatics.GetBindable(Static.LastHoverSoundPlaybackTime).IsDefault); + Assert.IsFalse(sessionStatics.GetBindable(Static.SeasonalBackgrounds).IsDefault); } } } diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs index 2b98c61e9b..7b5e1f4ec7 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs @@ -252,7 +252,7 @@ namespace osu.Game.Tests.Visual.Beatmaps [Test] public void TestNormal() { - createTestCase(beatmapSetInfo => new BeatmapCard(beatmapSetInfo)); + createTestCase(beatmapSetInfo => new BeatmapCardNormal(beatmapSetInfo)); } [Test] @@ -264,7 +264,7 @@ namespace osu.Game.Tests.Visual.Beatmaps [Test] public void TestHoverState() { - AddStep("create cards", () => Child = createContent(OverlayColourScheme.Blue, s => new BeatmapCard(s))); + AddStep("create cards", () => Child = createContent(OverlayColourScheme.Blue, s => new BeatmapCardNormal(s))); AddStep("Hover card", () => InputManager.MoveMouseTo(firstCard())); AddWaitStep("wait for potential state change", 5); @@ -281,10 +281,10 @@ namespace osu.Game.Tests.Visual.Beatmaps AddWaitStep("wait for potential state change", 5); AddAssert("card is still expanded", () => firstCard().Expanded.Value); - AddStep("Hover away", () => InputManager.MoveMouseTo(this.ChildrenOfType().Last())); + AddStep("Hover away", () => InputManager.MoveMouseTo(this.ChildrenOfType().Last())); AddUntilStep("card is not expanded", () => !firstCard().Expanded.Value); - BeatmapCard firstCard() => this.ChildrenOfType().First(); + BeatmapCardNormal firstCard() => this.ChildrenOfType().First(); } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index a3b4c4c86c..6adcdcfcb2 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -3,284 +3,78 @@ #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.Sprites; -using osu.Framework.Localisation; -using osu.Game.Beatmaps.Drawables.Cards.Statistics; -using osu.Game.Graphics; +using osu.Framework.Input.Events; 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; namespace osu.Game.Beatmaps.Drawables.Cards { - public class BeatmapCard : BeatmapCardBase + public abstract class BeatmapCard : OsuClickableContainer { - protected override Drawable IdleContent => idleBottomContent; - protected override Drawable DownloadInProgressContent => downloadProgressBar; + public const float TRANSITION_DURATION = 400; + public const float CORNER_RADIUS = 10; - private const float width = 408; - private const float height = 100; + public IBindable Expanded { get; } - [Cached] - private readonly BeatmapCardContent content; + protected readonly APIBeatmapSet BeatmapSet; + protected readonly Bindable FavouriteState; - private BeatmapCardThumbnail thumbnail = null!; - private CollapsibleButtonContainer buttonContainer = null!; + protected abstract Drawable IdleContent { get; } + protected abstract Drawable DownloadInProgressContent { get; } - private FillFlowContainer statisticsContainer = null!; + protected readonly BeatmapDownloadTracker DownloadTracker; - private FillFlowContainer idleBottomContent = null!; - private BeatmapCardDownloadProgressBar downloadProgressBar = null!; - - [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; - - public BeatmapCard(APIBeatmapSet beatmapSet, bool allowExpansion = true) - : base(beatmapSet, allowExpansion) + protected BeatmapCard(APIBeatmapSet beatmapSet, bool allowExpansion = true) + : base(HoverSampleSet.Submit) { - content = new BeatmapCardContent(height); + Expanded = new BindableBool { Disabled = !allowExpansion }; + + BeatmapSet = beatmapSet; + FavouriteState = new Bindable(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount)); + DownloadTracker = new BeatmapDownloadTracker(beatmapSet); } - [BackgroundDependencyLoader] - private void load() + [BackgroundDependencyLoader(true)] + private void load(BeatmapSetOverlay? beatmapSetOverlay) { - Width = width; - Height = height; + Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(BeatmapSet.OnlineID); - FillFlowContainer leftIconArea = null!; - GridContainer titleContainer = null!; - GridContainer artistContainer = null!; - - Child = content.With(c => - { - c.MainContent = new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - thumbnail = new BeatmapCardThumbnail(BeatmapSet) - { - Name = @"Left (icon) area", - Size = new Vector2(height), - Padding = new MarginPadding { Right = CORNER_RADIUS }, - Child = leftIconArea = new FillFlowContainer - { - Margin = new MarginPadding(5), - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(1) - } - }, - buttonContainer = new CollapsibleButtonContainer(BeatmapSet) - { - X = height - CORNER_RADIUS, - Width = width - height + CORNER_RADIUS, - FavouriteState = { BindTarget = FavouriteState }, - ButtonsCollapsedWidth = CORNER_RADIUS, - ButtonsExpandedWidth = 30, - ButtonsPadding = new MarginPadding { Vertical = 17.5f }, - Children = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - 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, - 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) - } - }, - downloadProgressBar = new BeatmapCardDownloadProgressBar - { - RelativeSizeAxes = Axes.X, - Height = 6, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - State = { BindTarget = DownloadTracker.State }, - Progress = { BindTarget = DownloadTracker.Progress } - } - } - } - } - } - } - }; - c.ExpandedContent = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = 10, Vertical = 13 }, - Child = new BeatmapCardDifficultyList(BeatmapSet) - }; - c.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 } - }; - } + AddInternal(DownloadTracker); } - private LocalisableString createArtistText() + protected override void LoadComplete() { - var romanisableArtist = new RomanisableString(BeatmapSet.ArtistUnicode, BeatmapSet.Artist); - return BeatmapsetsStrings.ShowDetailsByArtist(romanisableArtist); + base.LoadComplete(); + + DownloadTracker.State.BindValueChanged(_ => UpdateState()); + Expanded.BindValueChanged(_ => UpdateState(), true); + FinishTransforms(true); } - private IEnumerable createStatistics() + protected override bool OnHover(HoverEvent e) { - var hypesStatistic = HypesStatistic.CreateFor(BeatmapSet); - if (hypesStatistic != null) - yield return hypesStatistic; - - var nominationsStatistic = NominationsStatistic.CreateFor(BeatmapSet); - if (nominationsStatistic != null) - yield return nominationsStatistic; - - yield return new FavouritesStatistic(BeatmapSet) { Current = FavouriteState }; - yield return new PlayCountStatistic(BeatmapSet); - - var dateStatistic = BeatmapCardDateStatistic.CreateFor(BeatmapSet); - if (dateStatistic != null) - yield return dateStatistic; + UpdateState(); + return base.OnHover(e); } - protected override void UpdateState() + protected override void OnHoverLost(HoverLostEvent e) { - base.UpdateState(); + UpdateState(); + base.OnHoverLost(e); + } - bool showDetails = IsHovered || Expanded.Value; + protected virtual void UpdateState() + { + bool showProgress = DownloadTracker.State.Value == DownloadState.Downloading || DownloadTracker.State.Value == DownloadState.Importing; - buttonContainer.ShowDetails.Value = showDetails; - thumbnail.Dimmed.Value = showDetails; - - statisticsContainer.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint); + 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/BeatmapCardBase.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardBase.cs deleted file mode 100644 index 77df0809bc..0000000000 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardBase.cs +++ /dev/null @@ -1,80 +0,0 @@ -// 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 9c43c16100..497283bc64 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 = BeatmapCardBase.CORNER_RADIUS, + CornerRadius = BeatmapCard.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 = BeatmapCardBase.CORNER_RADIUS, + CornerRadius = BeatmapCard.CORNER_RADIUS, Masking = true, }, dropdownContent = new HoverHandlingContainer @@ -91,7 +91,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards borderContainer = new Container { RelativeSizeAxes = Axes.Both, - CornerRadius = BeatmapCardBase.CORNER_RADIUS, + CornerRadius = BeatmapCard.CORNER_RADIUS, Masking = true, BorderThickness = 3, Child = new Box @@ -143,9 +143,9 @@ namespace osu.Game.Beatmaps.Drawables.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); + 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); content.TweenEdgeEffectTo(new EdgeEffectParameters { @@ -154,7 +154,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards Radius = 10, Colour = Colour4.Black.Opacity(Expanded.Value ? 0.3f : 0f), Hollow = true, - }, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + }, BeatmapCard.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 6388e1698c..392f5d1bfa 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, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + background.FadeColour(Dimmed.Value ? colourProvider.Background4 : colourProvider.Background2, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); var gradient = ColourInfo.GradientHorizontal(Colour4.White.Opacity(0), Colour4.White.Opacity(0.2f)); - cover.FadeColour(gradient, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + cover.FadeColour(gradient, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); }); } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDownloadProgressBar.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDownloadProgressBar.cs index b80c5221ab..ffb4e0c540 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, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + foregroundFill.FadeColour(colours.Yellow, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); break; } } private void progressChanged() { - foreground.ResizeWidthTo((float)progress.Value, progress.Value > 0 ? BeatmapCardBase.TRANSITION_DURATION : 0, Easing.OutQuint); + foreground.ResizeWidthTo((float)progress.Value, progress.Value > 0 ? BeatmapCard.TRANSITION_DURATION : 0, Easing.OutQuint); } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs index 308cba9f0f..7f70423ff9 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtra.cs @@ -20,7 +20,7 @@ using osu.Game.Resources.Localisation.Web; namespace osu.Game.Beatmaps.Drawables.Cards { - public class BeatmapCardExtra : BeatmapCardBase + public class BeatmapCardExtra : BeatmapCard { protected override Drawable IdleContent => idleBottomContent; protected override Drawable DownloadInProgressContent => downloadProgressBar; diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs new file mode 100644 index 0000000000..edca310db9 --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardNormal.cs @@ -0,0 +1,286 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +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.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.BeatmapSet; +using osuTK; +using osu.Game.Resources.Localisation.Web; + +namespace osu.Game.Beatmaps.Drawables.Cards +{ + public class BeatmapCardNormal : BeatmapCard + { + protected override Drawable IdleContent => idleBottomContent; + protected override Drawable DownloadInProgressContent => downloadProgressBar; + + private const float width = 408; + private const float height = 100; + + [Cached] + private readonly BeatmapCardContent content; + + private BeatmapCardThumbnail thumbnail = null!; + private CollapsibleButtonContainer buttonContainer = null!; + + private FillFlowContainer statisticsContainer = null!; + + private FillFlowContainer idleBottomContent = null!; + private BeatmapCardDownloadProgressBar downloadProgressBar = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + public BeatmapCardNormal(APIBeatmapSet beatmapSet, bool allowExpansion = true) + : base(beatmapSet, allowExpansion) + { + content = new BeatmapCardContent(height); + } + + [BackgroundDependencyLoader] + private void load() + { + Width = width; + Height = height; + + FillFlowContainer leftIconArea = null!; + GridContainer titleContainer = null!; + GridContainer artistContainer = null!; + + Child = content.With(c => + { + c.MainContent = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + thumbnail = new BeatmapCardThumbnail(BeatmapSet) + { + Name = @"Left (icon) area", + Size = new Vector2(height), + Padding = new MarginPadding { Right = CORNER_RADIUS }, + Child = leftIconArea = new FillFlowContainer + { + Margin = new MarginPadding(5), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(1) + } + }, + buttonContainer = new CollapsibleButtonContainer(BeatmapSet) + { + X = height - CORNER_RADIUS, + Width = width - height + CORNER_RADIUS, + FavouriteState = { BindTarget = FavouriteState }, + ButtonsCollapsedWidth = CORNER_RADIUS, + ButtonsExpandedWidth = 30, + ButtonsPadding = new MarginPadding { Vertical = 17.5f }, + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + 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, + 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) + } + }, + downloadProgressBar = new BeatmapCardDownloadProgressBar + { + RelativeSizeAxes = Axes.X, + Height = 6, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + State = { BindTarget = DownloadTracker.State }, + Progress = { BindTarget = DownloadTracker.Progress } + } + } + } + } + } + } + }; + c.ExpandedContent = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 10, Vertical = 13 }, + Child = new BeatmapCardDifficultyList(BeatmapSet) + }; + c.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 } + }; + } + } + + private LocalisableString createArtistText() + { + var romanisableArtist = new RomanisableString(BeatmapSet.ArtistUnicode, BeatmapSet.Artist); + return BeatmapsetsStrings.ShowDetailsByArtist(romanisableArtist); + } + + private IEnumerable createStatistics() + { + var hypesStatistic = HypesStatistic.CreateFor(BeatmapSet); + if (hypesStatistic != null) + yield return hypesStatistic; + + var nominationsStatistic = NominationsStatistic.CreateFor(BeatmapSet); + if (nominationsStatistic != null) + yield return nominationsStatistic; + + yield return new FavouritesStatistic(BeatmapSet) { Current = FavouriteState }; + yield return new PlayCountStatistic(BeatmapSet); + + var dateStatistic = BeatmapCardDateStatistic.CreateFor(BeatmapSet); + if (dateStatistic != null) + yield return dateStatistic; + } + + protected override void UpdateState() + { + base.UpdateState(); + + bool showDetails = IsHovered || Expanded.Value; + + buttonContainer.ShowDetails.Value = showDetails; + thumbnail.Dimmed.Value = showDetails; + + statisticsContainer.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint); + } + } +} diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardThumbnail.cs index e6b305552a..f11a5916e1 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, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); - cover.FadeColour(shouldDim ? OsuColour.Gray(0.2f) : Color4.White, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + playButton.FadeTo(shouldDim ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); + cover.FadeColour(shouldDim ? OsuColour.Gray(0.2f) : Color4.White, BeatmapCard.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 02c9ea640a..e362e3abeb 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, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + content.FadeColour(isHovered ? HoverColour : IdleColour, BeatmapCard.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 d7ba4af21e..c94e335e8f 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(BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + this.FadeOut(BeatmapCard.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(BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + this.FadeIn(BeatmapCard.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 b039eb6f10..9a6a3c01b7 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, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + this.FadeTo(state.Value == DownloadState.LocallyAvailable ? 1 : 0, BeatmapCard.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 6d66d5001a..f7bab26666 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, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + icon.FadeTo(loading ? 0 : 1, BeatmapCard.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 e1a630182f..3a2cb80a8d 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 = BeatmapCardBase.CORNER_RADIUS; + CornerRadius = BeatmapCard.CORNER_RADIUS; InternalChildren = new Drawable[] { @@ -133,7 +133,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards { Name = @"Main content", RelativeSizeAxes = Axes.Y, - CornerRadius = BeatmapCardBase.CORNER_RADIUS, + CornerRadius = BeatmapCard.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, BeatmapCardBase.TRANSITION_DURATION, Easing.OutQuint); + mainArea.ResizeWidthTo(targetWidth, 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); + 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) { diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index ac94c39bd2..837ee7e634 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Bindables; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; @@ -13,18 +12,27 @@ 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)); - ensureDefault(SetDefault(Static.LowBatteryNotificationShownOnce, false)); - ensureDefault(SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null)); - ensureDefault(SetDefault(Static.SeasonalBackgrounds, null)); + SetDefault(Static.LoginOverlayDisplayed, false); + SetDefault(Static.MutedAudioNotificationShownOnce, false); + SetDefault(Static.LowBatteryNotificationShownOnce, false); + SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); + SetDefault(Static.SeasonalBackgrounds, null); } - private void ensureDefault(Bindable bindable) => bindable.SetDefault(); + /// + /// Revert statics to their defaults after being idle for appropriate amount of time. + /// + /// + /// This only affects a subset of statics which the user would expect to have reset after a break. + /// + public void ResetAfterInactivity() + { + GetBindable(Static.LoginOverlayDisplayed).SetDefault(); + GetBindable(Static.MutedAudioNotificationShownOnce).SetDefault(); + GetBindable(Static.LowBatteryNotificationShownOnce).SetDefault(); + } } public enum Static diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a4471b56b9..c5a465ae96 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -680,7 +680,7 @@ namespace osu.Game sessionIdleTracker.IsIdle.BindValueChanged(idle => { if (idle.NewValue) - SessionStatics.ResetValues(); + SessionStatics.ResetAfterInactivity(); }); Add(sessionIdleTracker); diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 6b27dbf847..c4d1d62250 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays private Drawable currentContent; private Container panelTarget; - private FillFlowContainer foundContent; + private FillFlowContainer foundContent; private NotFoundDrawable notFoundContent; private SupporterRequiredDrawable supporterRequiredContent; private BeatmapListingFilterControl filterControl; @@ -78,7 +78,7 @@ namespace osu.Game.Overlays Padding = new MarginPadding { Horizontal = 20 }, Children = new Drawable[] { - foundContent = new FillFlowContainer(), + foundContent = new FillFlowContainer(), notFoundContent = new NotFoundDrawable(), supporterRequiredContent = new SupporterRequiredDrawable(), } @@ -135,7 +135,7 @@ namespace osu.Game.Overlays return; } - var newPanels = searchResult.Results.Select(b => new BeatmapCard(b) + var newPanels = searchResult.Results.Select(b => new BeatmapCardNormal(b) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -152,7 +152,7 @@ namespace osu.Game.Overlays // spawn new children with the contained so we only clear old content at the last moment. // reverse ID flow is required for correct Z-ordering of the cards' expandable content (last card should be front-most). - var content = new ReverseChildIDFillFlowContainer + var content = new ReverseChildIDFillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index e46e503dfa..d39074bd49 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); protected override Drawable CreateDrawableItem(APIBeatmapSet model) => model.OnlineID > 0 - ? new BeatmapCard(model) + ? new BeatmapCardNormal(model) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs index bcfc2499b9..fb01656c24 100644 --- a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs +++ b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs @@ -136,12 +136,12 @@ namespace osu.Game.Overlays.Rankings { new ScoresTable(1, response.Users), // reverse ID flow is required for correct Z-ordering of the cards' expandable content (last card should be front-most). - new ReverseChildIDFillFlowContainer + new ReverseChildIDFillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Spacing = new Vector2(10), - Children = response.BeatmapSets.Select(b => new BeatmapCard(b) + Children = response.BeatmapSets.Select(b => new BeatmapCardNormal(b) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Screens/Play/SoloSpectator.cs b/osu.Game/Screens/Play/SoloSpectator.cs index ba5663bfa3..b530965269 100644 --- a/osu.Game/Screens/Play/SoloSpectator.cs +++ b/osu.Game/Screens/Play/SoloSpectator.cs @@ -228,7 +228,7 @@ namespace osu.Game.Screens.Play onlineBeatmapRequest.Success += beatmapSet => Schedule(() => { this.beatmapSet = beatmapSet; - beatmapPanelContainer.Child = new BeatmapCard(this.beatmapSet, allowExpansion: false); + beatmapPanelContainer.Child = new BeatmapCardNormal(this.beatmapSet, allowExpansion: false); checkForAutomaticDownload(); });