diff --git a/osu.Game.Tests/Visual/SongSelectV2/BeatmapCarouselFilterGroupingTest.cs b/osu.Game.Tests/Visual/SongSelectV2/BeatmapCarouselFilterGroupingTest.cs index ca2c5d415e..7f34d7a901 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/BeatmapCarouselFilterGroupingTest.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/BeatmapCarouselFilterGroupingTest.cs @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 ..beatmap4.Beatmaps ]; - var results = await runGrouping(GroupMode.NoGrouping, beatmapSets); + var results = await runGrouping(GroupMode.None, beatmapSets); Assert.That(results.Select(r => r.Model).OfType(), Is.EquivalentTo(beatmapSets)); Assert.That(results.Select(r => r.Model).OfType(), Is.EquivalentTo(allBeatmaps)); assertTotal(results, beatmapSets.Count + allBeatmaps.Length); diff --git a/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs b/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs index c7c56f30f4..75996fe158 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/SongSelectTestScene.cs @@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 SelectedMods.SetDefault(); Config.SetValue(OsuSetting.SongSelectSortingMode, SortMode.Title); - Config.SetValue(OsuSetting.SongSelectGroupMode, GroupMode.NoGrouping); + Config.SetValue(OsuSetting.SongSelectGroupMode, GroupMode.None); SongSelect = null!; }); diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarousel.cs index 616748a9d5..d9bb612a32 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarousel.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 [Explicit] public void TestSorting() { - SortAndGroupBy(SortMode.Artist, GroupMode.NoGrouping); + SortAndGroupBy(SortMode.Artist, GroupMode.None); SortAndGroupBy(SortMode.Difficulty, GroupMode.Difficulty); SortAndGroupBy(SortMode.Artist, GroupMode.Artist); } @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 public void TestLoadingDisplay() { AddStep("induce slow filtering", () => Carousel.FilterDelay = 2000); - SortAndGroupBy(SortMode.Artist, GroupMode.NoGrouping); + SortAndGroupBy(SortMode.Artist, GroupMode.None); } [Test] @@ -125,7 +125,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 CreateCarousel(); RemoveAllBeatmaps(); - SortAndGroupBy(SortMode.Difficulty, GroupMode.NoGrouping); + SortAndGroupBy(SortMode.Difficulty, GroupMode.None); AddBeatmaps(1, fixedDifficultiesPerSet: 1); AddUntilStep("single item is shown", () => this.ChildrenOfType().Count(), () => Is.EqualTo(1)); } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs index e72a373d63..9d5d5ddedb 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselNoGrouping.cs @@ -243,7 +243,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 AddBeatmaps(2, 3); WaitForDrawablePanels(); - SortAndGroupBy(SortMode.Difficulty, GroupMode.NoGrouping); + SortAndGroupBy(SortMode.Difficulty, GroupMode.None); WaitForFiltering(); AddUntilStep("standalone panels displayed", () => GetVisiblePanels().Any()); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneTabControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneTabControl.cs index bf75d07c2c..49eb1f092c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneTabControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneTabControl.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.UserInterface Position = new Vector2(275, 5) }); - filter.PinItem(GroupMode.NoGrouping); + filter.PinItem(GroupMode.None); filter.PinItem(GroupMode.LastPlayed); filter.Current.ValueChanged += grouping => diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index c815b2f9a9..df3e7d88af 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -47,7 +47,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1); SetDefault(OsuSetting.DisplayStarsMaximum, 10.1, 0, 10.1, 0.1); - SetDefault(OsuSetting.SongSelectGroupMode, GroupMode.NoGrouping); + SetDefault(OsuSetting.SongSelectGroupMode, GroupMode.None); SetDefault(OsuSetting.SongSelectSortingMode, SortMode.Title); SetDefault(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); diff --git a/osu.Game/Graphics/Carousel/Carousel.cs b/osu.Game/Graphics/Carousel/Carousel.cs index 9646c86dd6..68ce07bf00 100644 --- a/osu.Game/Graphics/Carousel/Carousel.cs +++ b/osu.Game/Graphics/Carousel/Carousel.cs @@ -617,13 +617,13 @@ namespace osu.Game.Graphics.Carousel { var item = carouselItems[i]; - updateItemYPosition(item, ref lastVisible, ref yPos); - if (CheckModelEquality(item.Model, currentKeyboardSelection.Model!)) currentKeyboardSelection = new Selection(currentKeyboardSelection.Model, item, item.CarouselYPosition, i); if (CheckModelEquality(item.Model, currentSelection.Model!)) currentSelection = new Selection(currentSelection.Model, item, item.CarouselYPosition, i); + + updateItemYPosition(item, ref lastVisible, ref yPos); } // If a keyboard selection is currently made, we want to keep the view stable around the selection. diff --git a/osu.Game/Screens/Footer/ScreenFooter.cs b/osu.Game/Screens/Footer/ScreenFooter.cs index 3907907158..1baa4ae0ef 100644 --- a/osu.Game/Screens/Footer/ScreenFooter.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -49,8 +50,13 @@ namespace osu.Game.Screens.Footer private Container hiddenButtonsContainer = null!; private LogoTrackingContainer logoTrackingContainer = null!; + // TODO: This has some weird update logic local in this class, but it only works for overlay containers. + // This is not what we want. The footer is to be displayed on *screens* with different colour schemes. + // It needs to update on screen switch. + // + // For now it's locked to Blue to match song select (the most prominent usage). [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); public ScreenFooter(BackReceptor? receptor = null) { @@ -167,6 +173,7 @@ namespace osu.Game.Screens.Footer temporarilyHiddenButtons.Clear(); overlays.Clear(); + this.HidePopover(); clearActiveOverlayContainer(); var oldButtons = buttonsFlow.ToArray(); @@ -312,6 +319,8 @@ namespace osu.Game.Screens.Footer private void showOverlay(OverlayContainer overlay) { + this.HidePopover(); + foreach (var o in overlays.Where(o => o != overlay)) o.Hide(); diff --git a/osu.Game/Screens/Select/Filter/GroupMode.cs b/osu.Game/Screens/Select/Filter/GroupMode.cs index 9f693177d8..b3a4f36c91 100644 --- a/osu.Game/Screens/Select/Filter/GroupMode.cs +++ b/osu.Game/Screens/Select/Filter/GroupMode.cs @@ -7,8 +7,8 @@ namespace osu.Game.Screens.Select.Filter { public enum GroupMode { - [Description("No Grouping")] - NoGrouping, + [Description("None")] + None, [Description("Artist")] Artist, @@ -19,8 +19,8 @@ namespace osu.Game.Screens.Select.Filter [Description("BPM")] BPM, - [Description("Collections")] - Collections, + // [Description("Collections")] + // Collections, [Description("Date Added")] DateAdded, @@ -31,17 +31,17 @@ namespace osu.Game.Screens.Select.Filter [Description("Difficulty")] Difficulty, - [Description("Favourites")] - Favourites, + // [Description("Favourites")] + // Favourites, [Description("Length")] Length, - [Description("My Maps")] - MyMaps, + // [Description("My Maps")] + // MyMaps, - [Description("Rank Achieved")] - RankAchieved, + // [Description("Rank Achieved")] + // RankAchieved, [Description("Ranked Status")] RankedStatus, diff --git a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs index e007ae54ce..6ee780b603 100644 --- a/osu.Game/Screens/SelectV2/BeatmapCarousel.cs +++ b/osu.Game/Screens/SelectV2/BeatmapCarousel.cs @@ -60,9 +60,25 @@ namespace osu.Game.Screens.SelectV2 if ((top.Model is GroupDefinition) ^ (bottom.Model is GroupDefinition)) return SPACING * 2; - // Beatmap difficulty panels do not overlap with themselves or any other panel. - if (grouping.BeatmapSetsGroupedTogether && (top.Model is BeatmapInfo || bottom.Model is BeatmapInfo)) - return SPACING; + if (grouping.BeatmapSetsGroupedTogether) + { + // Give some space around the expanded beatmap set, at the top.. + if (bottom.Model is BeatmapSetInfo && bottom.IsExpanded) + return SPACING * 2; + + // ..and the bottom. + if (top.Model is BeatmapInfo && bottom.Model is BeatmapSetInfo) + return SPACING * 2; + + // Beatmap difficulty panels do not overlap with themselves or any other panel. + if (top.Model is BeatmapInfo || bottom.Model is BeatmapInfo) + return SPACING; + } + else + { + if (top == CurrentSelectionItem || bottom == CurrentSelectionItem) + return SPACING * 2; + } return -SPACING; } diff --git a/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs b/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs index 7d449661ab..8720378ad6 100644 --- a/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs +++ b/osu.Game/Screens/SelectV2/BeatmapCarouselFilterGrouping.cs @@ -139,7 +139,7 @@ namespace osu.Game.Screens.SelectV2 { switch (criteria.Group) { - case GroupMode.NoGrouping: + case GroupMode.None: return new List { new GroupMapping(null, items) }; case GroupMode.Artist: @@ -199,21 +199,19 @@ namespace osu.Game.Screens.SelectV2 return defineGroupByLength(length); }, items); - case GroupMode.Collections: - // TODO: needs implementation - goto case GroupMode.NoGrouping; - - case GroupMode.Favourites: - // TODO: needs implementation - goto case GroupMode.NoGrouping; - - case GroupMode.MyMaps: - // TODO: needs implementation - goto case GroupMode.NoGrouping; - - case GroupMode.RankAchieved: - // TODO: needs implementation - goto case GroupMode.NoGrouping; + // TODO: need implementation + // + // case GroupMode.Collections: + // goto case GroupMode.None; + // + // case GroupMode.Favourites: + // goto case GroupMode.None; + // + // case GroupMode.MyMaps: + // goto case GroupMode.None; + // + // case GroupMode.RankAchieved: + // goto case GroupMode.None; default: throw new ArgumentOutOfRangeException(); diff --git a/osu.Game/Screens/SelectV2/BeatmapLeaderboardScore.cs b/osu.Game/Screens/SelectV2/BeatmapLeaderboardScore.cs index dd4e2d4f9c..6a810a83b4 100644 --- a/osu.Game/Screens/SelectV2/BeatmapLeaderboardScore.cs +++ b/osu.Game/Screens/SelectV2/BeatmapLeaderboardScore.cs @@ -117,6 +117,15 @@ namespace osu.Game.Screens.SelectV2 private readonly bool sheared; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + { + var inputRectangle = DrawRectangle; + + inputRectangle = inputRectangle.Inflate(new MarginPadding { Vertical = BeatmapLeaderboardWedge.SPACING_BETWEEN_SCORES / 2 }); + + return inputRectangle.Contains(ToLocalSpace(screenSpacePos)); + } + public BeatmapLeaderboardScore(ScoreInfo score, bool sheared = true) { this.score = score; diff --git a/osu.Game/Screens/SelectV2/BeatmapLeaderboardWedge.cs b/osu.Game/Screens/SelectV2/BeatmapLeaderboardWedge.cs index 1f63e3de9f..e3d52adef5 100644 --- a/osu.Game/Screens/SelectV2/BeatmapLeaderboardWedge.cs +++ b/osu.Game/Screens/SelectV2/BeatmapLeaderboardWedge.cs @@ -3,12 +3,15 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.PolygonExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; @@ -26,11 +29,14 @@ using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Select.Leaderboards; using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.SelectV2 { public partial class BeatmapLeaderboardWedge : VisibilityContainer { + public const float SPACING_BETWEEN_SCORES = 4; + public IBindable Scope { get; } = new Bindable(); public IBindable FilterBySelectedMods { get; } = new BindableBool(); @@ -254,10 +260,10 @@ namespace osu.Game.Screens.SelectV2 foreach (var d in loadedScores) { - d.Y = (BeatmapLeaderboardScore.HEIGHT + 4f) * i; + d.Y = (BeatmapLeaderboardScore.HEIGHT + SPACING_BETWEEN_SCORES) * i; // This is a bit of a weird one. We're already in a sheared state and don't want top-level - // shear applied, but still need the `BeatmapLeadeboardScore` to be in "sheared" mode (see ctor). + // shear applied, but still need the `BeatmapLeaderboardScore` to be in "sheared" mode (see ctor). d.Shear = Vector2.Zero; scoresContainer.Add(d); @@ -350,6 +356,59 @@ namespace osu.Game.Screens.SelectV2 placeholder.FadeInFromZero(300, Easing.OutQuint); } + #region Fade handling + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + const int height = BeatmapLeaderboardScore.HEIGHT; + + float fadeBottom = (float)(scoresScroll.Current + scoresScroll.DrawHeight); + float fadeTop = (float)(scoresScroll.Current); + + if (!scoresScroll.IsScrolledToStart()) + fadeTop += height; + + foreach (var c in scoresContainer) + { + float topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scoresContainer).Y; + float bottomY = topY + height; + + bool requireBottomFade = bottomY >= fadeBottom; + bool requireTopFade = topY < fadeTop; + + if (!requireBottomFade && !requireTopFade) + { + c.Colour = Color4.White; + continue; + } + + if (topY > fadeBottom + height || bottomY < fadeTop - height) + { + c.Colour = Color4.Transparent; + continue; + } + + if (requireBottomFade) + { + c.Colour = ColourInfo.GradientVertical( + Color4.White.Opacity(Math.Min(1 - (topY - fadeBottom) / height, 1)), + Color4.White.Opacity(Math.Min(1 - (bottomY - fadeBottom) / height, 1))); + } + else + { + Debug.Assert(requireTopFade); + + c.Colour = ColourInfo.GradientVertical( + Color4.White.Opacity(Math.Min(1 - (fadeTop - topY) / height, 1)), + Color4.White.Opacity(Math.Min(1 - (fadeTop - bottomY) / height, 1))); + } + } + } + + #endregion + private Placeholder? getPlaceholderFor(LeaderboardState state) { switch (state) diff --git a/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyDisplay.cs b/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyDisplay.cs index 8362f5b6a7..734d768241 100644 --- a/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyDisplay.cs +++ b/osu.Game/Screens/SelectV2/BeatmapTitleWedge_DifficultyDisplay.cs @@ -253,7 +253,7 @@ namespace osu.Game.Screens.SelectV2 mapperText.Text = beatmap.Value.Metadata.Author.Username; } - starRatingDisplay.Current = (Bindable)difficultyCache.GetBindableDifficulty(beatmap.Value.BeatmapInfo, cancellationSource.Token, 200); + starRatingDisplay.Current = (Bindable)difficultyCache.GetBindableDifficulty(beatmap.Value.BeatmapInfo, cancellationSource.Token, SongSelect.SELECTION_DEBOUNCE); updateCountStatistics(cancellationSource.Token); updateDifficultyStatistics(); diff --git a/osu.Game/Screens/SelectV2/FilterControl.cs b/osu.Game/Screens/SelectV2/FilterControl.cs index 8b360688fa..05429c2c12 100644 --- a/osu.Game/Screens/SelectV2/FilterControl.cs +++ b/osu.Game/Screens/SelectV2/FilterControl.cs @@ -17,7 +17,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Localisation; -using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select; @@ -142,9 +141,9 @@ namespace osu.Game.Screens.SelectV2 RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, ColumnDimensions = new[] { - new Dimension(maxSize: 210), + new Dimension(maxSize: 180), new Dimension(GridSizeMode.Absolute, 5), - new Dimension(maxSize: 230), + new Dimension(maxSize: 180), new Dimension(GridSizeMode.Absolute, 5), new Dimension(), }, @@ -152,14 +151,14 @@ namespace osu.Game.Screens.SelectV2 { new[] { - sortDropdown = new ShearedDropdown(SortStrings.Default) + sortDropdown = new ShearedDropdown("Sort") { RelativeSizeAxes = Axes.X, Items = Enum.GetValues(), }, Empty(), // todo: pending localisation - groupDropdown = new ShearedDropdown("Group by") + groupDropdown = new ShearedDropdown("Group") { RelativeSizeAxes = Axes.X, Items = Enum.GetValues(), diff --git a/osu.Game/Screens/SelectV2/NoResultsPlaceholder.cs b/osu.Game/Screens/SelectV2/NoResultsPlaceholder.cs index 8caa559550..46f8859255 100644 --- a/osu.Game/Screens/SelectV2/NoResultsPlaceholder.cs +++ b/osu.Game/Screens/SelectV2/NoResultsPlaceholder.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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -20,10 +21,14 @@ namespace osu.Game.Screens.SelectV2 { public partial class NoResultsPlaceholder : VisibilityContainer { + public Action? RequestClearFilterText { get; init; } + private FilterCriteria? filter; private LinkFlowContainer textFlow = null!; + private SpriteIcon icon = null!; + [Resolved] private BeatmapManager beatmaps { get; set; } = null!; @@ -50,8 +55,7 @@ namespace osu.Game.Screens.SelectV2 [BackgroundDependencyLoader] private void load() { - Width = 400; - AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.Both; Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -61,11 +65,13 @@ namespace osu.Game.Screens.SelectV2 new FillFlowContainer { Direction = FillDirection.Vertical, - RelativeSizeAxes = Axes.X, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 300, AutoSizeAxes = Axes.Y, Children = new Drawable[] { - new SpriteIcon + icon = new SpriteIcon { Icon = FontAwesome.Solid.Ghost, Anchor = Anchor.TopCentre, @@ -78,7 +84,7 @@ namespace osu.Game.Screens.SelectV2 Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Font = OsuFont.Style.Title, - Text = "No beatmaps found" + Text = "No matching beatmaps" }, textFlow = new LinkFlowContainer { @@ -115,6 +121,9 @@ namespace osu.Game.Screens.SelectV2 this.ScaleTo(0.9f) .ScaleTo(1f, 1000, Easing.OutQuint); + icon.ScaleTo(new Vector2(-1, 1)) + .ScaleTo(new Vector2(1, 1), 500, Easing.InOutSine); + textFlow.FadeInFromZero(800, Easing.OutQuint); textFlow.Clear(); @@ -131,6 +140,18 @@ namespace osu.Game.Screens.SelectV2 textFlow.AddParagraph("No beatmaps match your filter criteria!"); textFlow.AddParagraph(string.Empty); + if (!string.IsNullOrEmpty(filter?.SearchText)) + { + addBulletPoint(); + textFlow.AddText("Try "); + textFlow.AddLink("clearing", () => + { + RequestClearFilterText?.Invoke(); + }); + + textFlow.AddText(" your current search criteria."); + } + if (filter?.UserStarDifficulty.HasFilter == true) { addBulletPoint(); diff --git a/osu.Game/Screens/SelectV2/Panel.cs b/osu.Game/Screens/SelectV2/Panel.cs index 12b9613039..f17567f9ba 100644 --- a/osu.Game/Screens/SelectV2/Panel.cs +++ b/osu.Game/Screens/SelectV2/Panel.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.SelectV2 { private const float corner_radius = 10; - private const float active_x_offset = 50f; + private const float active_x_offset = 25f; protected const float DURATION = 400; @@ -34,7 +34,6 @@ namespace osu.Game.Screens.SelectV2 private Box backgroundBorder = null!; private Box backgroundGradient = null!; - private Box backgroundAccentGradient = null!; private Container backgroundLayerHorizontalPadding = null!; private Container backgroundContainer = null!; private Container iconContainer = null!; @@ -61,6 +60,7 @@ namespace osu.Game.Screens.SelectV2 public Color4? AccentColour { + get => accentColour; set { accentColour = value; @@ -123,12 +123,6 @@ namespace osu.Game.Screens.SelectV2 { RelativeSizeAxes = Axes.Both, }, - // TODO: this is only used by beatmap panels and should NOT be in this class. - // it's wasting fill rate. - backgroundAccentGradient = new Box - { - RelativeSizeAxes = Axes.Both, - }, backgroundContainer = new Container { RelativeSizeAxes = Axes.Both, @@ -243,7 +237,6 @@ namespace osu.Game.Screens.SelectV2 { var backgroundColour = accentColour ?? Color4.White; - backgroundAccentGradient.Colour = ColourInfo.GradientHorizontal(backgroundColour.Opacity(0.25f), backgroundColour.Opacity(0f)); backgroundBorder.Colour = backgroundColour; selectionLayer.Colour = ColourInfo.GradientHorizontal(backgroundColour.Opacity(0), backgroundColour.Opacity(0.5f)); @@ -269,10 +262,15 @@ namespace osu.Game.Screens.SelectV2 float x = PanelXOffset + corner_radius; if (!Expanded.Value && !Selected.Value) - x += active_x_offset; + { + if (this is PanelBeatmap) + x += active_x_offset * 2; + else + x += active_x_offset * 4; + } if (!KeyboardSelected.Value) - x += active_x_offset * 0.5f; + x += active_x_offset; TopLevelContent.MoveToX(x, DURATION, Easing.OutQuint); } diff --git a/osu.Game/Screens/SelectV2/PanelBeatmap.cs b/osu.Game/Screens/SelectV2/PanelBeatmap.cs index 31cebbd152..e785448c9a 100644 --- a/osu.Game/Screens/SelectV2/PanelBeatmap.cs +++ b/osu.Game/Screens/SelectV2/PanelBeatmap.cs @@ -7,12 +7,16 @@ using System.Diagnostics; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Carousel; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -36,10 +40,15 @@ namespace osu.Game.Screens.SelectV2 private PanelLocalRankDisplay localRank = null!; private OsuSpriteText difficultyText = null!; private OsuSpriteText authorText = null!; + private FillFlowContainer mainFill = null!; private IBindable? starDifficultyBindable; private CancellationTokenSource? starDifficultyCancellationSource; + private Box backgroundAccentGradient = null!; + + private TrianglesV2 triangles = null!; + [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -58,6 +67,11 @@ namespace osu.Game.Screens.SelectV2 [Resolved] private ISongSelect? songSelect { get; set; } + public PanelBeatmap() + { + PanelXOffset = 60; + } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) { var inputRectangle = TopLevelContent.DrawRectangle; @@ -78,80 +92,108 @@ namespace osu.Game.Screens.SelectV2 Icon = difficultyIcon = new ConstrainedIconContainer { - Size = new Vector2(16f), - Margin = new MarginPadding { Horizontal = 5f }, + Size = new Vector2(9f), + Margin = new MarginPadding { Left = 2.5f, Right = 1.5f }, Colour = colourProvider.Background5, }; - Content.Children = new[] + Background = new Container { - new FillFlowContainer + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Padding = new MarginPadding { Left = 10f }, - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] + backgroundAccentGradient = new Box { - new FillFlowContainer + RelativeSizeAxes = Axes.Both, + }, + triangles = new TrianglesV2 + { + ScaleAdjust = 1.2f, + Thickness = 0.01f, + Velocity = 0.3f, + RelativeSizeAxes = Axes.Both, + }, + } + }; + + Content.Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Spacing = new Vector2(3), + Margin = new MarginPadding { Left = 5 }, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + localRank = new PanelLocalRankDisplay + { + Scale = new Vector2(0.8f), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + }, + mainFill = new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - Direction = FillDirection.Horizontal, - Spacing = new Vector2(3, 0), - AutoSizeAxes = Axes.Both, - Children = new Drawable[] + new FillFlowContainer { - localRank = new PanelLocalRankDisplay + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Bottom = 4 }, + Children = new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Scale = new Vector2(0.65f) - }, - starRatingDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Small, animated: true) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Scale = new Vector2(0.875f), - }, - starCounter = new StarCounter - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Scale = new Vector2(0.4f) + keyCountText = new OsuSpriteText + { + Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Alpha = 0, + }, + difficultyText = new OsuSpriteText + { + Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Right = 3f }, + }, + authorText = new OsuSpriteText + { + Colour = colourProvider.Content2, + Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft + } } - } - }, - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - AutoSizeAxes = Axes.Both, - Children = new[] + }, + new FillFlowContainer { - keyCountText = new OsuSpriteText + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3), + AutoSizeAxes = Axes.Both, + Children = new Drawable[] { - Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Alpha = 0, + starRatingDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Small, animated: true) + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Scale = new Vector2(0.875f), + }, + starCounter = new StarCounter + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Scale = new Vector2(0.4f) + } }, - difficultyText = new OsuSpriteText - { - Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Right = 3f }, - }, - authorText = new OsuSpriteText - { - Colour = colourProvider.Content2, - Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft - } } } } - }, + } }; } @@ -210,12 +252,10 @@ namespace osu.Game.Screens.SelectV2 var beatmap = (BeatmapInfo)Item.Model; starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, starDifficultyCancellationSource.Token, SongSelect.SELECTION_DEBOUNCE); - starDifficultyBindable.BindValueChanged(_ => + starDifficultyBindable.BindValueChanged(starDifficulty => { - var starDifficulty = starDifficultyBindable?.Value ?? default; - - starRatingDisplay.Current.Value = starDifficulty; - starCounter.Current = (float)starDifficulty.Stars; + starRatingDisplay.Current.Value = starDifficulty.NewValue; + starCounter.Current = (float)starDifficulty.NewValue.Stars; }, true); } @@ -231,14 +271,21 @@ namespace osu.Game.Screens.SelectV2 // Dirty hack to make sure we don't take up spacing in parent fill flow when not displaying a rank. // I can't find a better way to do this. - starRatingDisplay.Margin = new MarginPadding { Left = 1 / starRatingDisplay.Scale.X * (localRank.HasRank ? 0 : -3) }; + mainFill.Margin = new MarginPadding { Left = 1 / starRatingDisplay.Scale.X * (localRank.HasRank ? 0 : -3) }; var diffColour = starRatingDisplay.DisplayedDifficultyColour; - AccentColour = diffColour; - starCounter.Colour = diffColour; + if (AccentColour != diffColour) + { + AccentColour = diffColour; + starCounter.Colour = diffColour; - difficultyIcon.Colour = starRatingDisplay.DisplayedStars.Value > OsuColour.STAR_DIFFICULTY_DEFINED_COLOUR_CUTOFF ? colours.Orange1 : colourProvider.Background5; + backgroundAccentGradient.Colour = ColourInfo.GradientHorizontal(diffColour.Opacity(0.25f), diffColour.Opacity(0f)); + + difficultyIcon.Colour = starRatingDisplay.DisplayedStars.Value > OsuColour.STAR_DIFFICULTY_DEFINED_COLOUR_CUTOFF ? colours.Orange1 : colourProvider.Background5; + + triangles.Colour = ColourInfo.GradientVertical(diffColour.Opacity(0.25f), diffColour.Opacity(0f)); + } } private void updateKeyCount() diff --git a/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs b/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs index ce9513a8a1..d461653dcb 100644 --- a/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs +++ b/osu.Game/Screens/SelectV2/PanelBeatmapStandalone.cs @@ -63,13 +63,29 @@ namespace osu.Game.Screens.SelectV2 private BeatmapSetOnlineStatusPill statusPill = null!; private ConstrainedIconContainer difficultyIcon = null!; - private FillFlowContainer difficultyLine = null!; private StarRatingDisplay starRatingDisplay = null!; private StarCounter starCounter = null!; private PanelLocalRankDisplay localRank = null!; private OsuSpriteText keyCountText = null!; private OsuSpriteText difficultyText = null!; private OsuSpriteText authorText = null!; + private FillFlowContainer mainFill = null!; + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + { + var inputRectangle = TopLevelContent.DrawRectangle; + + if (Selected.Value) + { + // Cover the gaps introduced by the spacing between BeatmapPanels so that clicks will not fall through the carousel. + // + // Caveat is that for simplicity, we are covering the full spacing, so panels with frontmost depth will have a slightly + // larger hit target. + inputRectangle = inputRectangle.Inflate(new MarginPadding { Vertical = BeatmapCarousel.SPACING * 2 }); + } + + return inputRectangle.Contains(TopLevelContent.ToLocalSpace(screenSpacePos)); + } public PanelBeatmapStandalone() { @@ -83,8 +99,8 @@ namespace osu.Game.Screens.SelectV2 Icon = difficultyIcon = new ConstrainedIconContainer { - Size = new Vector2(16), - Margin = new MarginPadding { Horizontal = 5f }, + Size = new Vector2(12), + Margin = new MarginPadding { Left = 4f, Right = 3f }, Colour = colourProvider.Background5, }; @@ -95,93 +111,105 @@ namespace osu.Game.Screens.SelectV2 Content.Child = new FillFlowContainer { + AutoSizeAxes = Axes.Both, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Padding = new MarginPadding { Left = 10f }, - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, + Spacing = new Vector2(3), + Margin = new MarginPadding { Left = 5 }, + Direction = FillDirection.Horizontal, Children = new Drawable[] { - titleText = new OsuSpriteText + localRank = new PanelLocalRankDisplay { - Font = OsuFont.Style.Heading2.With(typeface: Typeface.TorusAlternate, weight: FontWeight.Bold), + Scale = new Vector2(0.8f), + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, }, - artistText = new OsuSpriteText + mainFill = new FillFlowContainer { - Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold), - Padding = new MarginPadding { Top = -2 }, - }, - difficultyLine = new FillFlowContainer - { - Direction = FillDirection.Horizontal, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Bottom = 2 }, AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = 4 }, Children = new Drawable[] { - statusPill = new BeatmapSetOnlineStatusPill + titleText = new OsuSpriteText { - Animated = false, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - TextSize = OsuFont.Style.Caption2.Size, - Margin = new MarginPadding { Right = 5f }, + Font = OsuFont.Style.Heading2.With(typeface: Typeface.TorusAlternate, weight: FontWeight.Bold), }, - updateButton = new PanelUpdateBeatmapButton + artistText = new OsuSpriteText { - Scale = new Vector2(0.7f), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Right = 5f, Top = -2f }, - }, - keyCountText = new OsuSpriteText - { - Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Alpha = 0, - }, - difficultyText = new OsuSpriteText - { - Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding { Right = 3f }, - }, - authorText = new OsuSpriteText - { - Colour = colourProvider.Content2, Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold), - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft + Padding = new MarginPadding { Top = -2 }, + }, + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 2, Bottom = 2 }, + Children = new Drawable[] + { + statusPill = new BeatmapSetOnlineStatusPill + { + Animated = false, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + TextSize = OsuFont.Style.Caption2.Size, + Margin = new MarginPadding { Right = 4f }, + }, + updateButton = new PanelUpdateBeatmapButton + { + Scale = new Vector2(0.8f), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Right = 4f, Bottom = -1f }, + }, + keyCountText = new OsuSpriteText + { + Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Alpha = 0, + }, + difficultyText = new OsuSpriteText + { + Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Right = 3f }, + }, + authorText = new OsuSpriteText + { + Colour = colourProvider.Content2, + Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft + } + } + }, + new FillFlowContainer + { + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3), + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + starRatingDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Small, animated: true) + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Scale = new Vector2(0.875f), + }, + starCounter = new StarCounter + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Scale = new Vector2(0.4f) + } + }, } } - }, - new FillFlowContainer - { - Direction = FillDirection.Horizontal, - Spacing = new Vector2(3), - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - localRank = new PanelLocalRankDisplay - { - Scale = new Vector2(0.65f), - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - }, - starRatingDisplay = new StarRatingDisplay(default, StarRatingDisplaySize.Small, animated: true) - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Scale = new Vector2(0.875f), - }, - starCounter = new StarCounter - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Scale = new Vector2(0.4f) - } - }, } } }; @@ -229,9 +257,9 @@ namespace osu.Game.Screens.SelectV2 localRank.Beatmap = beatmap; difficultyText.Text = beatmap.DifficultyName; authorText.Text = BeatmapsetsStrings.ShowDetailsMappedBy(beatmap.Metadata.Author.Username); - difficultyLine.Show(); computeStarRating(); + updateKeyCount(); } protected override void FreeAfterUse() @@ -257,12 +285,10 @@ namespace osu.Game.Screens.SelectV2 var beatmap = (BeatmapInfo)Item.Model; starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, starDifficultyCancellationSource.Token, SongSelect.SELECTION_DEBOUNCE); - starDifficultyBindable.BindValueChanged(_ => + starDifficultyBindable.BindValueChanged(starDifficulty => { - var starDifficulty = starDifficultyBindable?.Value ?? default; - - starRatingDisplay.Current.Value = starDifficulty; - starCounter.Current = (float)starDifficulty.Stars; + starRatingDisplay.Current.Value = starDifficulty.NewValue; + starCounter.Current = (float)starDifficulty.NewValue.Stars; }, true); } @@ -278,7 +304,7 @@ namespace osu.Game.Screens.SelectV2 // Dirty hack to make sure we don't take up spacing in parent fill flow when not displaying a rank. // I can't find a better way to do this. - starRatingDisplay.Margin = new MarginPadding { Left = 1 / starRatingDisplay.Scale.X * (localRank.HasRank ? 0 : -3) }; + mainFill.Margin = new MarginPadding { Left = 1 / starRatingDisplay.Scale.X * (localRank.HasRank ? 0 : -3) }; var diffColour = starRatingDisplay.DisplayedDifficultyColour; diff --git a/osu.Game/Screens/SelectV2/SongSelect.cs b/osu.Game/Screens/SelectV2/SongSelect.cs index 6feaeb0ef2..44f3984c64 100644 --- a/osu.Game/Screens/SelectV2/SongSelect.cs +++ b/osu.Game/Screens/SelectV2/SongSelect.cs @@ -71,15 +71,20 @@ namespace osu.Game.Screens.SelectV2 /// protected bool ControlGlobalMusic { get; init; } = true; - private readonly ModSelectOverlay modSelectOverlay = new UserModSelectOverlay(OverlayColourScheme.Aquamarine) + // Colour scheme for mod overlay is left as default (green) to match mods button. + // Not sure about this, but we'll iterate based on feedback. + private readonly ModSelectOverlay modSelectOverlay = new UserModSelectOverlay { ShowPresets = true, }; private ModSpeedHotkeyHandler modSpeedHotkeyHandler = null!; + // Blue is the most neutral choice, so I'm using that for now. + // Purple makes the most sense to match the "gameplay" flow, but it's a bit too strong for the current design. + // TODO: Colour scheme choice should probably be customisable by the user. [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue); private BeatmapCarousel carousel = null!; @@ -149,7 +154,7 @@ namespace osu.Game.Screens.SelectV2 { new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 660), new Dimension(), - new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 580), + new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 620), }, Content = new[] { @@ -197,8 +202,13 @@ namespace osu.Game.Screens.SelectV2 new Container { RelativeSizeAxes = Axes.Both, - Children = new CompositeDrawable[] + Children = new Drawable[] { + new Box + { + Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.0f), Color4.Black.Opacity(0.5f)), + RelativeSizeAxes = Axes.Both, + }, new Container { RelativeSizeAxes = Axes.Both, @@ -219,7 +229,10 @@ namespace osu.Game.Screens.SelectV2 RequestRecommendedSelection = selectRecommendedBeatmap, NewItemsPresented = newItemsPresented, }, - noResultsPlaceholder = new NoResultsPlaceholder(), + noResultsPlaceholder = new NoResultsPlaceholder + { + RequestClearFilterText = () => filterControl.Search(string.Empty) + } } }, filterControl = new FilterControl