1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 17:43:05 +08:00

Merge pull request #15948 from bdach/beatmap-card/difficulty-dropdown

Add difficulty dropdown to beatmap card
This commit is contained in:
Dean Herbert 2021-12-07 23:40:06 +09:00 committed by GitHub
commit d6e68feadc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 550 additions and 215 deletions

View File

@ -6,12 +6,14 @@ using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables.Cards;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
@ -227,7 +229,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer
Child = new ReverseChildIDFillFlowContainer<Drawable>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
@ -248,6 +250,17 @@ namespace osu.Game.Tests.Visual.Beatmaps
}
[Test]
public void TestNormal() => createTestCase(beatmapSetInfo => new BeatmapCard(beatmapSetInfo));
public void TestNormal()
{
createTestCase(beatmapSetInfo => new BeatmapCard(beatmapSetInfo));
AddToggleStep("toggle expanded state", expanded =>
{
var card = this.ChildrenOfType<BeatmapCard>().Last();
if (!card.Expanded.Disabled)
card.Expanded.Value = expanded;
});
AddToggleStep("disable/enable expansion", disabled => this.ChildrenOfType<BeatmapCard>().ForEach(card => card.Expanded.Disabled = disabled));
}
}
}

View File

@ -31,10 +31,12 @@ namespace osu.Game.Beatmaps.Drawables.Cards
public class BeatmapCard : OsuClickableContainer
{
public const float TRANSITION_DURATION = 400;
public const float CORNER_RADIUS = 10;
public Bindable<bool> Expanded { get; } = new BindableBool();
private const float width = 408;
private const float height = 100;
private const float corner_radius = 10;
private const float icon_area_width = 30;
private readonly APIBeatmapSet beatmapSet;
@ -42,6 +44,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards
private readonly BeatmapDownloadTracker downloadTracker;
private BeatmapCardContent content = null!;
private BeatmapCardThumbnail thumbnail = null!;
private Container rightAreaBackground = null!;
@ -73,242 +77,241 @@ namespace osu.Game.Beatmaps.Drawables.Cards
{
Width = width;
Height = height;
CornerRadius = corner_radius;
Masking = true;
FillFlowContainer leftIconArea;
GridContainer titleContainer;
GridContainer artistContainer;
InternalChildren = new Drawable[]
InternalChild = content = new BeatmapCardContent(height)
{
downloadTracker,
rightAreaBackground = new Container
MainContent = new Container
{
RelativeSizeAxes = Axes.Y,
Width = icon_area_width + 2 * corner_radius,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
// workaround for masking artifacts at the top & bottom of card,
// which become especially visible on downloaded beatmaps (when the icon area has a lime background).
Padding = new MarginPadding { Vertical = 1 },
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Colour4.White
},
},
thumbnail = new BeatmapCardThumbnail(beatmapSet)
{
Name = @"Left (icon) area",
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)
}
},
new Container
{
Name = @"Right (button) area",
Width = 30,
RelativeSizeAxes = Axes.Y,
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
Padding = new MarginPadding { Vertical = 17.5f },
Child = rightAreaButtons = new Container<BeatmapCardIconButton>
{
RelativeSizeAxes = Axes.Both,
Children = new BeatmapCardIconButton[]
{
new FavouriteButton(beatmapSet)
{
Current = favouriteState,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
new DownloadButton(beatmapSet)
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
State = { BindTarget = downloadTracker.State }
},
new GoToBeatmapButton(beatmapSet)
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
State = { BindTarget = downloadTracker.State }
}
}
}
},
mainContent = new Container
{
Name = @"Main content",
X = height - corner_radius,
Height = height,
CornerRadius = corner_radius,
Masking = true,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
mainContentBackground = new BeatmapCardContentBackground(beatmapSet)
downloadTracker,
rightAreaBackground = new Container
{
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
RelativeSizeAxes = Axes.Y,
Width = icon_area_width + 2 * CORNER_RADIUS,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
// workaround for masking artifacts at the top & bottom of card,
// which become especially visible on downloaded beatmaps (when the icon area has a lime background).
Padding = new MarginPadding { Vertical = 1 },
Child = new Box
{
Horizontal = 10,
Vertical = 4
RelativeSizeAxes = Axes.Both,
Colour = Colour4.White
},
Direction = FillDirection.Vertical,
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
{
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);
}),
Margin = new MarginPadding(5),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(1)
}
},
new Container
{
Name = @"Bottom content",
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Padding = new MarginPadding
Name = @"Right (button) area",
Width = 30,
RelativeSizeAxes = Axes.Y,
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
Padding = new MarginPadding { Vertical = 17.5f },
Child = rightAreaButtons = new Container<BeatmapCardIconButton>
{
Horizontal = 10,
Vertical = 4
},
RelativeSizeAxes = Axes.Both,
Children = new BeatmapCardIconButton[]
{
new FavouriteButton(beatmapSet)
{
Current = favouriteState,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
new DownloadButton(beatmapSet)
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
State = { BindTarget = downloadTracker.State }
},
new GoToBeatmapButton(beatmapSet)
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
State = { BindTarget = downloadTracker.State }
}
}
}
},
mainContent = new Container
{
Name = @"Main content",
X = height - CORNER_RADIUS,
Height = height,
CornerRadius = CORNER_RADIUS,
Masking = true,
Children = new Drawable[]
{
idleBottomContent = new FillFlowContainer
mainContentBackground = new BeatmapCardContentBackground(beatmapSet)
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Horizontal = 10,
Vertical = 4
},
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 3),
AlwaysPresent = true,
Children = new Drawable[]
{
statisticsContainer = new FillFlowContainer<BeatmapCardStatistic>
titleContainer = new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Alpha = 0,
AlwaysPresent = true,
ChildrenEnumerable = createStatistics()
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(4, 0),
Children = new Drawable[]
ColumnDimensions = new[]
{
new BeatmapSetOnlineStatusPill
new Dimension(),
new Dimension(GridSizeMode.AutoSize)
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize)
},
Content = new[]
{
new[]
{
AutoSizeAxes = Axes.Both,
Status = beatmapSet.Status,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
},
new DifficultySpectrumDisplay(beatmapSet)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
DotSize = new Vector2(6, 12)
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);
}),
}
},
downloadProgressBar = new BeatmapCardDownloadProgressBar
new Container
{
Name = @"Bottom content",
RelativeSizeAxes = Axes.X,
Height = 6,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
State = { BindTarget = downloadTracker.State },
Progress = { BindTarget = downloadTracker.Progress }
AutoSizeAxes = Axes.Y,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Padding = new MarginPadding
{
Horizontal = 10,
Vertical = 4
},
Children = new Drawable[]
{
idleBottomContent = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 3),
AlwaysPresent = true,
Children = new Drawable[]
{
statisticsContainer = new FillFlowContainer<BeatmapCardStatistic>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Alpha = 0,
AlwaysPresent = true,
ChildrenEnumerable = createStatistics()
},
new BeatmapCardExtraInfoRow(beatmapSet)
{
Hovered = _ =>
{
content.ScheduleShow();
return false;
},
Unhovered = _ => content.ScheduleHide()
}
}
},
downloadProgressBar = new BeatmapCardDownloadProgressBar
{
RelativeSizeAxes = Axes.X,
Height = 6,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
State = { BindTarget = downloadTracker.State },
Progress = { BindTarget = downloadTracker.Progress }
}
}
}
}
}
}
}
},
ExpandedContent = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = 10, Vertical = 13 },
Child = new BeatmapCardDifficultyList(beatmapSet)
},
Expanded = { BindTarget = Expanded }
};
if (beatmapSet.HasVideo)
@ -344,7 +347,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards
{
base.LoadComplete();
downloadTracker.State.BindValueChanged(_ => updateState(), true);
downloadTracker.State.BindValueChanged(_ => updateState());
Expanded.BindValueChanged(_ => updateState(), true);
FinishTransforms(true);
}
@ -386,19 +390,21 @@ namespace osu.Game.Beatmaps.Drawables.Cards
private void updateState()
{
float targetWidth = width - height;
if (IsHovered)
targetWidth = targetWidth - icon_area_width + corner_radius;
bool showDetails = IsHovered || Expanded.Value;
thumbnail.Dimmed.Value = IsHovered;
float targetWidth = width - height;
if (showDetails)
targetWidth = targetWidth - icon_area_width + CORNER_RADIUS;
thumbnail.Dimmed.Value = showDetails;
mainContent.ResizeWidthTo(targetWidth, TRANSITION_DURATION, Easing.OutQuint);
mainContentBackground.Dimmed.Value = IsHovered;
mainContentBackground.Dimmed.Value = showDetails;
statisticsContainer.FadeTo(IsHovered ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
statisticsContainer.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
rightAreaBackground.FadeColour(downloadTracker.State.Value == DownloadState.LocallyAvailable ? colours.Lime0 : colourProvider.Background3, TRANSITION_DURATION, Easing.OutQuint);
rightAreaButtons.FadeTo(IsHovered ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
rightAreaButtons.FadeTo(showDetails ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
foreach (var button in rightAreaButtons)
{

View File

@ -0,0 +1,231 @@
// 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.
#nullable enable
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Framework.Threading;
using osu.Framework.Utils;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Beatmaps.Drawables.Cards
{
public class BeatmapCardContent : CompositeDrawable
{
public Drawable MainContent
{
set => bodyContent.Child = value;
}
public Drawable ExpandedContent
{
set => dropdownScroll.Child = value;
}
public Bindable<bool> Expanded { get; } = new BindableBool();
private readonly Box background;
private readonly Container content;
private readonly Container bodyContent;
private readonly Container dropdownContent;
private readonly OsuScrollContainer dropdownScroll;
private readonly Container borderContainer;
public BeatmapCardContent(float height)
{
RelativeSizeAxes = Axes.X;
Height = height;
InternalChild = content = new HoverHandlingContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
CornerRadius = BeatmapCard.CORNER_RADIUS,
Masking = true,
Unhovered = _ => checkForHide(),
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both
},
bodyContent = new Container
{
RelativeSizeAxes = Axes.X,
Height = height,
CornerRadius = BeatmapCard.CORNER_RADIUS,
Masking = true,
},
dropdownContent = new HoverHandlingContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = height },
Alpha = 0,
Hovered = _ =>
{
keep();
return true;
},
Unhovered = _ => checkForHide(),
Child = dropdownScroll = new ExpandedContentScrollContainer
{
RelativeSizeAxes = Axes.X,
ScrollbarVisible = false
}
},
borderContainer = new Container
{
RelativeSizeAxes = Axes.Both,
CornerRadius = BeatmapCard.CORNER_RADIUS,
Masking = true,
BorderThickness = 3,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
}
};
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
background.Colour = colourProvider.Background2;
borderContainer.BorderColour = colourProvider.Highlight1;
}
protected override void LoadComplete()
{
base.LoadComplete();
Expanded.BindValueChanged(_ => updateState(), true);
FinishTransforms(true);
}
private ScheduledDelegate? scheduledExpandedChange;
public void ScheduleShow()
{
scheduledExpandedChange?.Cancel();
if (Expanded.Disabled || Expanded.Value)
return;
scheduledExpandedChange = Scheduler.AddDelayed(() =>
{
if (!Expanded.Disabled)
Expanded.Value = true;
}, 100);
}
public void ScheduleHide()
{
scheduledExpandedChange?.Cancel();
if (Expanded.Disabled || !Expanded.Value)
return;
scheduledExpandedChange = Scheduler.AddDelayed(() =>
{
if (!Expanded.Disabled)
Expanded.Value = false;
}, 500);
}
private void checkForHide()
{
if (Expanded.Disabled)
return;
if (content.IsHovered || dropdownContent.IsHovered)
return;
scheduledExpandedChange?.Cancel();
Expanded.Value = false;
}
private void keep()
{
if (Expanded.Disabled)
return;
scheduledExpandedChange?.Cancel();
Expanded.Value = true;
}
private void updateState()
{
background.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
dropdownContent.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
borderContainer.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
content.TweenEdgeEffectTo(new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0, 2),
Radius = 10,
Colour = Colour4.Black.Opacity(Expanded.Value ? 0.3f : 0f),
Hollow = true,
}, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint);
}
private class ExpandedContentScrollContainer : OsuScrollContainer
{
public ExpandedContentScrollContainer()
{
ScrollbarVisible = false;
}
protected override void Update()
{
base.Update();
Height = Math.Min(Content.DrawHeight, 400);
}
private bool allowScroll => !Precision.AlmostEquals(DrawSize, Content.DrawSize);
protected override bool OnDragStart(DragStartEvent e)
{
if (!allowScroll)
return false;
return base.OnDragStart(e);
}
protected override void OnDrag(DragEvent e)
{
if (!allowScroll)
return;
base.OnDrag(e);
}
protected override void OnDragEnd(DragEndEvent e)
{
if (!allowScroll)
return;
base.OnDragEnd(e);
}
protected override bool OnScroll(ScrollEvent e)
{
if (!allowScroll)
return false;
return base.OnScroll(e);
}
}
}
}

View File

@ -0,0 +1,43 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API.Requests.Responses;
using osuTK;
namespace osu.Game.Beatmaps.Drawables.Cards
{
public class BeatmapCardExtraInfoRow : HoverHandlingContainer
{
public BeatmapCardExtraInfoRow(APIBeatmapSet beatmapSet)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(4, 0),
Children = new Drawable[]
{
new BeatmapSetOnlineStatusPill
{
AutoSizeAxes = Axes.Both,
Status = beatmapSet.Status,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
},
new DifficultySpectrumDisplay(beatmapSet)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
DotSize = new Vector2(6, 12)
}
}
};
}
}
}

View File

@ -0,0 +1,29 @@
// 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.
#nullable enable
using System;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
namespace osu.Game.Beatmaps.Drawables.Cards
{
public class HoverHandlingContainer : Container
{
public Func<HoverEvent, bool>? Hovered { get; set; }
public Action<HoverLostEvent>? Unhovered { get; set; }
protected override bool OnHover(HoverEvent e)
{
bool handledByBase = base.OnHover(e);
return Hovered?.Invoke(e) ?? handledByBase;
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
Unhovered?.Invoke(e);
}
}
}

View File

@ -151,7 +151,8 @@ namespace osu.Game.Overlays
}
// spawn new children with the contained so we only clear old content at the last moment.
var content = new FillFlowContainer<BeatmapCard>
// reverse ID flow is required for correct Z-ordering of the cards' expandable content (last card should be front-most).
var content = new ReverseChildIDFillFlowContainer<BeatmapCard>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,

View File

@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
@ -22,7 +23,7 @@ namespace osu.Game.Overlays.Profile
public abstract string Identifier { get; }
private readonly FillFlowContainer content;
private readonly FillFlowContainer<Drawable> content;
private readonly Box background;
private readonly Box underscore;
@ -79,7 +80,9 @@ namespace osu.Game.Overlays.Profile
}
}
},
content = new FillFlowContainer
// reverse ID flow is required for correct Z-ordering of the content (last item should be front-most).
// particularly important in BeatmapsSection, as it uses beatmap cards, which have expandable overhanging content.
content = new ReverseChildIDFillFlowContainer<Drawable>
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,

View File

@ -13,6 +13,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests.Responses;
using osuTK;
@ -26,7 +27,7 @@ namespace osu.Game.Overlays.Profile.Sections
protected int VisiblePages;
protected int ItemsPerPage;
protected FillFlowContainer ItemsContainer { get; private set; }
protected ReverseChildIDFillFlowContainer<Drawable> ItemsContainer { get; private set; }
private APIRequest<List<TModel>> retrievalRequest;
private CancellationTokenSource loadCancellation;
@ -48,11 +49,15 @@ namespace osu.Game.Overlays.Profile.Sections
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
ItemsContainer = new FillFlowContainer
// reverse ID flow is required for correct Z-ordering of the items (last item should be front-most).
// particularly important in PaginatedBeatmapContainer, as it uses beatmap cards, which have expandable overhanging content.
ItemsContainer = new ReverseChildIDFillFlowContainer<Drawable>
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Spacing = new Vector2(0, 2),
// ensure the container and its contents are in front of the "more" button.
Depth = float.MinValue
},
moreButton = new ShowMoreButton
{

View File

@ -135,7 +135,8 @@ namespace osu.Game.Overlays.Rankings
Children = new Drawable[]
{
new ScoresTable(1, response.Users),
new FillFlowContainer
// reverse ID flow is required for correct Z-ordering of the cards' expandable content (last card should be front-most).
new ReverseChildIDFillFlowContainer<BeatmapCard>
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,

View File

@ -228,7 +228,10 @@ namespace osu.Game.Screens.Play
onlineBeatmapRequest.Success += beatmapSet => Schedule(() =>
{
this.beatmapSet = beatmapSet;
beatmapPanelContainer.Child = new BeatmapCard(this.beatmapSet);
beatmapPanelContainer.Child = new BeatmapCard(this.beatmapSet)
{
Expanded = { Disabled = true }
};
checkForAutomaticDownload();
});