diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 66b9fa990a..453869f721 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -43,6 +44,29 @@ namespace osu.Game.Tests.Visual.SongSelect this.rulesets = rulesets; } + [Test] + public void TestBeatmapWithOnlineUpdates() + { + var testBeatmapSetInfo = TestResources.CreateTestBeatmapSetInfo(); + + createCarousel(new List + { + testBeatmapSetInfo, + }); + + AddAssert("update button not visible", () => !carousel.ChildrenOfType().Any()); + + AddStep("update online hash", () => + { + testBeatmapSetInfo.Beatmaps.First().OnlineMD5Hash = "different hash"; + testBeatmapSetInfo.Beatmaps.First().LastOnlineUpdate = DateTimeOffset.Now; + + carousel.UpdateBeatmapSet(testBeatmapSetInfo); + }); + + AddUntilStep("update button visible", () => carousel.ChildrenOfType().Any()); + } + [Test] public void TestExternalRulesetChange() { @@ -825,7 +849,8 @@ namespace osu.Game.Tests.Visual.SongSelect checkVisibleItemCount(true, 15); } - private void loadBeatmaps(List beatmapSets = null, Func initialCriteria = null, Action carouselAdjust = null, int? count = null, bool randomDifficulties = false) + private void loadBeatmaps(List beatmapSets = null, Func initialCriteria = null, Action carouselAdjust = null, int? count = null, + bool randomDifficulties = false) { bool changed = false; diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 96d95b1a12..ead280a75e 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -93,5 +93,7 @@ namespace osu.Game.Beatmaps IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; IEnumerable IHasNamedFiles.Files => Files; + + public bool AllBeatmapsUpToDate => Beatmaps.All(b => b.MatchesOnlineVersion); } } diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index a6532ee145..50e30c68d5 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -153,17 +153,12 @@ namespace osu.Game.Screens.Select.Carousel { Direction = FillDirection.Horizontal, Spacing = new Vector2(4, 0), + Scale = new Vector2(0.8f), AutoSizeAxes = Axes.Both, Children = new Drawable[] { - new TopLocalRank(beatmapInfo) - { - Scale = new Vector2(0.8f), - }, - starCounter = new StarCounter - { - Scale = new Vector2(0.8f), - } + new TopLocalRank(beatmapInfo), + starCounter = new StarCounter() } } } diff --git a/osu.Game/Screens/Select/Carousel/SetPanelContent.cs b/osu.Game/Screens/Select/Carousel/SetPanelContent.cs index 577b3f5f64..050425f9f1 100644 --- a/osu.Game/Screens/Select/Carousel/SetPanelContent.cs +++ b/osu.Game/Screens/Select/Carousel/SetPanelContent.cs @@ -61,14 +61,15 @@ namespace osu.Game.Screens.Select.Carousel Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Top = 5 }, - Children = new Drawable[] + Spacing = new Vector2(5), + Children = new[] { + beatmapSet.AllBeatmapsUpToDate ? Empty() : new UpdateRequiredIcon(beatmapSet), new BeatmapSetOnlineStatusPill { AutoSizeAxes = Axes.Both, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Margin = new MarginPadding { Right = 5 }, TextSize = 11, TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, Status = beatmapSet.Status @@ -76,6 +77,8 @@ namespace osu.Game.Screens.Select.Carousel new FillFlowContainer { AutoSizeAxes = Axes.Both, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, Spacing = new Vector2(3), ChildrenEnumerable = getDifficultyIcons(), }, diff --git a/osu.Game/Screens/Select/Carousel/UpdateRequiredIcon.cs b/osu.Game/Screens/Select/Carousel/UpdateRequiredIcon.cs new file mode 100644 index 0000000000..42539f2836 --- /dev/null +++ b/osu.Game/Screens/Select/Carousel/UpdateRequiredIcon.cs @@ -0,0 +1,105 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; + +namespace osu.Game.Screens.Select.Carousel +{ + public class UpdateRequiredIcon : OsuAnimatedButton + { + private readonly BeatmapSetInfo beatmapSetInfo; + private SpriteIcon icon; + + public UpdateRequiredIcon(BeatmapSetInfo beatmapSetInfo) + { + this.beatmapSetInfo = beatmapSetInfo; + + AutoSizeAxes = Axes.Both; + + Anchor = Anchor.CentreLeft; + Origin = Anchor.CentreLeft; + } + + [Resolved] + private BeatmapModelDownloader beatmaps { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load() + { + const float icon_size = 14; + + Content.Anchor = Anchor.CentreLeft; + Content.Origin = Anchor.CentreLeft; + + Content.AddRange(new Drawable[] + { + new FillFlowContainer + { + Padding = new MarginPadding { Horizontal = 5, Vertical = 3 }, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(4), + Children = new Drawable[] + { + new Container + { + Size = new Vector2(icon_size), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Children = new Drawable[] + { + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.SyncAlt, + Size = new Vector2(icon_size), + }, + } + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Font = OsuFont.Default.With(weight: FontWeight.Bold), + Text = "Update", + } + } + }, + }); + + TooltipText = "Update beatmap with online changes"; + + Action = () => beatmaps.Download(beatmapSetInfo); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + icon.Spin(4000, RotationDirection.Clockwise); + } + + protected override bool OnHover(HoverEvent e) + { + icon.Spin(400, RotationDirection.Clockwise); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + icon.Spin(4000, RotationDirection.Clockwise); + base.OnHoverLost(e); + } + } +}