mirror of
https://github.com/ppy/osu.git
synced 2025-02-13 19:12:54 +08:00
Add initial implementation of beatmap carousel no-results-placeholder
This commit is contained in:
parent
df9174ec00
commit
0d32c94104
@ -18,6 +18,7 @@ using osu.Game.Database;
|
|||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Online.Chat;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@ -80,6 +81,37 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
AddStep("delete all beatmaps", () => manager?.Delete());
|
AddStep("delete all beatmaps", () => manager?.Delete());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPlaceholderBeatmapPresence()
|
||||||
|
{
|
||||||
|
createSongSelect();
|
||||||
|
|
||||||
|
AddUntilStep("wait for placeholder visible", () => getPlaceholder()?.State.Value == Visibility.Visible);
|
||||||
|
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
AddUntilStep("wait for placeholder hidden", () => getPlaceholder()?.State.Value == Visibility.Hidden);
|
||||||
|
|
||||||
|
AddStep("delete all beatmaps", () => manager?.Delete());
|
||||||
|
AddUntilStep("wait for placeholder visible", () => getPlaceholder()?.State.Value == Visibility.Visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPlaceholderConvertSetting()
|
||||||
|
{
|
||||||
|
changeRuleset(2);
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
AddStep("change convert setting", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, false));
|
||||||
|
|
||||||
|
createSongSelect();
|
||||||
|
|
||||||
|
AddUntilStep("wait for placeholder visible", () => getPlaceholder()?.State.Value == Visibility.Visible);
|
||||||
|
|
||||||
|
AddStep("click link in placeholder", () => getPlaceholder().ChildrenOfType<DrawableLinkCompiler>().First().TriggerClick());
|
||||||
|
|
||||||
|
AddUntilStep("convert setting changed", () => config.Get<bool>(OsuSetting.ShowConvertedBeatmaps));
|
||||||
|
AddUntilStep("wait for placeholder visible", () => getPlaceholder()?.State.Value == Visibility.Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSingleFilterOnEnter()
|
public void TestSingleFilterOnEnter()
|
||||||
{
|
{
|
||||||
@ -941,6 +973,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.IndexOf(info);
|
private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.IndexOf(info);
|
||||||
|
|
||||||
|
private NoResultsPlaceholder getPlaceholder() => songSelect.ChildrenOfType<NoResultsPlaceholder>().FirstOrDefault();
|
||||||
|
|
||||||
private int getCurrentBeatmapIndex() => getBeatmapIndex(songSelect.Carousel.SelectedBeatmapSet, songSelect.Carousel.SelectedBeatmapInfo);
|
private int getCurrentBeatmapIndex() => getBeatmapIndex(songSelect.Carousel.SelectedBeatmapSet, songSelect.Carousel.SelectedBeatmapInfo);
|
||||||
|
|
||||||
private int getDifficultyIconIndex(DrawableCarouselBeatmapSet set, FilterableDifficultyIcon icon)
|
private int getDifficultyIconIndex(DrawableCarouselBeatmapSet set, FilterableDifficultyIcon icon)
|
||||||
|
@ -97,6 +97,8 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
protected readonly CarouselScrollContainer Scroll;
|
protected readonly CarouselScrollContainer Scroll;
|
||||||
|
|
||||||
|
private readonly NoResultsPlaceholder noResultsPlaceholder;
|
||||||
|
|
||||||
private IEnumerable<CarouselBeatmapSet> beatmapSets => root.Children.OfType<CarouselBeatmapSet>();
|
private IEnumerable<CarouselBeatmapSet> beatmapSets => root.Children.OfType<CarouselBeatmapSet>();
|
||||||
|
|
||||||
// todo: only used for testing, maybe remove.
|
// todo: only used for testing, maybe remove.
|
||||||
@ -170,7 +172,8 @@ namespace osu.Game.Screens.Select
|
|||||||
Scroll = new CarouselScrollContainer
|
Scroll = new CarouselScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
}
|
},
|
||||||
|
noResultsPlaceholder = new NoResultsPlaceholder()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -648,8 +651,18 @@ namespace osu.Game.Screens.Select
|
|||||||
// First we iterate over all non-filtered carousel items and populate their
|
// First we iterate over all non-filtered carousel items and populate their
|
||||||
// vertical position data.
|
// vertical position data.
|
||||||
if (revalidateItems)
|
if (revalidateItems)
|
||||||
|
{
|
||||||
updateYPositions();
|
updateYPositions();
|
||||||
|
|
||||||
|
if (visibleItems.Count == 0)
|
||||||
|
{
|
||||||
|
noResultsPlaceholder.Filter = activeCriteria;
|
||||||
|
noResultsPlaceholder.Show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
noResultsPlaceholder.Hide();
|
||||||
|
}
|
||||||
|
|
||||||
// if there is a pending scroll action we apply it without animation and transfer the difference in position to the panels.
|
// if there is a pending scroll action we apply it without animation and transfer the difference in position to the panels.
|
||||||
// this is intentionally applied before updating the visible range below, to avoid animating new items (sourced from pool) from locations off-screen, as it looks bad.
|
// this is intentionally applied before updating the visible range below, to avoid animating new items (sourced from pool) from locations off-screen, as it looks bad.
|
||||||
if (pendingScrollOperation != PendingScrollOperation.None)
|
if (pendingScrollOperation != PendingScrollOperation.None)
|
||||||
|
122
osu.Game/Screens/Select/NoResultsPlaceholder.cs
Normal file
122
osu.Game/Screens/Select/NoResultsPlaceholder.cs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Online.Chat;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Select
|
||||||
|
{
|
||||||
|
public class NoResultsPlaceholder : CompositeDrawable
|
||||||
|
{
|
||||||
|
private FilterCriteria filter;
|
||||||
|
|
||||||
|
private LinkFlowContainer textFlow;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private BeatmapManager beatmaps { get; set; }
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private FirstRunSetupOverlay firstRunSetupOverlay { get; set; }
|
||||||
|
|
||||||
|
public FilterCriteria Filter
|
||||||
|
{
|
||||||
|
get => filter;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
filter = value;
|
||||||
|
Scheduler.AddOnce(updateText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 10;
|
||||||
|
|
||||||
|
Width = 300;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colours.Gray2,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new SpriteIcon
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.Regular.QuestionCircle,
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Margin = new MarginPadding(10),
|
||||||
|
Size = new Vector2(50),
|
||||||
|
},
|
||||||
|
textFlow = new LinkFlowContainer
|
||||||
|
{
|
||||||
|
Y = 70,
|
||||||
|
Padding = new MarginPadding(10),
|
||||||
|
TextAnchor = Anchor.TopCentre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Show()
|
||||||
|
{
|
||||||
|
this.FadeIn(600, Easing.OutQuint);
|
||||||
|
|
||||||
|
this.ScaleTo(0.8f)
|
||||||
|
.ScaleTo(1f, 1000, Easing.OutElastic);
|
||||||
|
|
||||||
|
Scheduler.AddOnce(updateText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Hide()
|
||||||
|
{
|
||||||
|
this.FadeOut(200, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateText()
|
||||||
|
{
|
||||||
|
textFlow.Clear();
|
||||||
|
|
||||||
|
if (beatmaps.QueryBeatmapSet(s => !s.Protected && !s.DeletePending) == null)
|
||||||
|
{
|
||||||
|
textFlow.AddParagraph("No beatmaps found!");
|
||||||
|
textFlow.AddParagraph(string.Empty);
|
||||||
|
|
||||||
|
textFlow.AddParagraph("Consider running the ");
|
||||||
|
textFlow.AddLink("first run setup", () => firstRunSetupOverlay?.Show());
|
||||||
|
textFlow.AddText(" to load or import some beatmaps!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
textFlow.AddParagraph("No beatmaps match your filter criteria!");
|
||||||
|
textFlow.AddParagraph(string.Empty);
|
||||||
|
|
||||||
|
// TODO: hint when beatmaps are available in another ruleset
|
||||||
|
// TODO: hint when beatmaps are available by toggling "show converted".
|
||||||
|
if (!string.IsNullOrEmpty(filter?.SearchText))
|
||||||
|
{
|
||||||
|
textFlow.AddParagraph("You can try ");
|
||||||
|
textFlow.AddLink("searching online", LinkAction.SearchBeatmapSet, filter.SearchText);
|
||||||
|
textFlow.AddText(" for this query.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user