mirror of
https://github.com/osukey/osukey.git
synced 2025-05-29 17:37:23 +09:00
Merge pull request #15948 from bdach/beatmap-card/difficulty-dropdown
Add difficulty dropdown to beatmap card
This commit is contained in:
commit
d6e68feadc
@ -6,12 +6,14 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Drawables.Cards;
|
using osu.Game.Beatmaps.Drawables.Cards;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
@ -227,7 +229,7 @@ namespace osu.Game.Tests.Visual.Beatmaps
|
|||||||
new BasicScrollContainer
|
new BasicScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = new FillFlowContainer
|
Child = new ReverseChildIDFillFlowContainer<Drawable>
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
@ -248,6 +250,17 @@ namespace osu.Game.Tests.Visual.Beatmaps
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,12 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
public class BeatmapCard : OsuClickableContainer
|
public class BeatmapCard : OsuClickableContainer
|
||||||
{
|
{
|
||||||
public const float TRANSITION_DURATION = 400;
|
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 width = 408;
|
||||||
private const float height = 100;
|
private const float height = 100;
|
||||||
private const float corner_radius = 10;
|
|
||||||
private const float icon_area_width = 30;
|
private const float icon_area_width = 30;
|
||||||
|
|
||||||
private readonly APIBeatmapSet beatmapSet;
|
private readonly APIBeatmapSet beatmapSet;
|
||||||
@ -42,6 +44,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
|
|
||||||
private readonly BeatmapDownloadTracker downloadTracker;
|
private readonly BeatmapDownloadTracker downloadTracker;
|
||||||
|
|
||||||
|
private BeatmapCardContent content = null!;
|
||||||
|
|
||||||
private BeatmapCardThumbnail thumbnail = null!;
|
private BeatmapCardThumbnail thumbnail = null!;
|
||||||
|
|
||||||
private Container rightAreaBackground = null!;
|
private Container rightAreaBackground = null!;
|
||||||
@ -73,242 +77,241 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
{
|
{
|
||||||
Width = width;
|
Width = width;
|
||||||
Height = height;
|
Height = height;
|
||||||
CornerRadius = corner_radius;
|
|
||||||
Masking = true;
|
|
||||||
|
|
||||||
FillFlowContainer leftIconArea;
|
FillFlowContainer leftIconArea;
|
||||||
GridContainer titleContainer;
|
GridContainer titleContainer;
|
||||||
GridContainer artistContainer;
|
GridContainer artistContainer;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChild = content = new BeatmapCardContent(height)
|
||||||
{
|
{
|
||||||
downloadTracker,
|
MainContent = new Container
|
||||||
rightAreaBackground = new Container
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Both,
|
||||||
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,
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
mainContentBackground = new BeatmapCardContentBackground(beatmapSet)
|
downloadTracker,
|
||||||
|
rightAreaBackground = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Y,
|
||||||
},
|
Width = icon_area_width + 2 * CORNER_RADIUS,
|
||||||
new FillFlowContainer
|
Anchor = Anchor.CentreRight,
|
||||||
{
|
Origin = Anchor.CentreRight,
|
||||||
RelativeSizeAxes = Axes.Both,
|
// workaround for masking artifacts at the top & bottom of card,
|
||||||
Padding = new MarginPadding
|
// 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,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Vertical = 4
|
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
|
Margin = new MarginPadding(5),
|
||||||
{
|
AutoSizeAxes = Axes.Both,
|
||||||
RelativeSizeAxes = Axes.X,
|
Direction = FillDirection.Horizontal,
|
||||||
AutoSizeAxes = Axes.Y,
|
Spacing = new Vector2(1)
|
||||||
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
|
new Container
|
||||||
{
|
{
|
||||||
Name = @"Bottom content",
|
Name = @"Right (button) area",
|
||||||
RelativeSizeAxes = Axes.X,
|
Width = 30,
|
||||||
AutoSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Anchor = Anchor.BottomLeft,
|
Origin = Anchor.TopRight,
|
||||||
Origin = Anchor.BottomLeft,
|
Anchor = Anchor.TopRight,
|
||||||
Padding = new MarginPadding
|
Padding = new MarginPadding { Vertical = 17.5f },
|
||||||
|
Child = rightAreaButtons = new Container<BeatmapCardIconButton>
|
||||||
{
|
{
|
||||||
Horizontal = 10,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Vertical = 4
|
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[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
idleBottomContent = new FillFlowContainer
|
mainContentBackground = new BeatmapCardContentBackground(beatmapSet)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.Both,
|
||||||
AutoSizeAxes = Axes.Y,
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Horizontal = 10,
|
||||||
|
Vertical = 4
|
||||||
|
},
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(0, 3),
|
|
||||||
AlwaysPresent = true,
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
statisticsContainer = new FillFlowContainer<BeatmapCardStatistic>
|
titleContainer = new GridContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Horizontal,
|
ColumnDimensions = new[]
|
||||||
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[]
|
|
||||||
{
|
{
|
||||||
new BeatmapSetOnlineStatusPill
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.AutoSize)
|
||||||
|
},
|
||||||
|
RowDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize)
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new[]
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
new OsuSpriteText
|
||||||
Status = beatmapSet.Status,
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Text = new RomanisableString(beatmapSet.TitleUnicode, beatmapSet.Title),
|
||||||
Origin = Anchor.CentreLeft
|
Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold),
|
||||||
},
|
RelativeSizeAxes = Axes.X,
|
||||||
new DifficultySpectrumDisplay(beatmapSet)
|
Truncate = true
|
||||||
{
|
},
|
||||||
Anchor = Anchor.CentreLeft,
|
Empty()
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
DotSize = new Vector2(6, 12)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
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,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = 6,
|
AutoSizeAxes = Axes.Y,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.BottomLeft,
|
||||||
State = { BindTarget = downloadTracker.State },
|
Padding = new MarginPadding
|
||||||
Progress = { BindTarget = downloadTracker.Progress }
|
{
|
||||||
|
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)
|
if (beatmapSet.HasVideo)
|
||||||
@ -344,7 +347,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
downloadTracker.State.BindValueChanged(_ => updateState(), true);
|
downloadTracker.State.BindValueChanged(_ => updateState());
|
||||||
|
Expanded.BindValueChanged(_ => updateState(), true);
|
||||||
FinishTransforms(true);
|
FinishTransforms(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,19 +390,21 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
|
|
||||||
private void updateState()
|
private void updateState()
|
||||||
{
|
{
|
||||||
float targetWidth = width - height;
|
bool showDetails = IsHovered || Expanded.Value;
|
||||||
if (IsHovered)
|
|
||||||
targetWidth = targetWidth - icon_area_width + corner_radius;
|
|
||||||
|
|
||||||
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);
|
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);
|
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)
|
foreach (var button in rightAreaButtons)
|
||||||
{
|
{
|
||||||
|
231
osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs
Normal file
231
osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs
Normal file
43
osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs
Normal file
29
osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -151,7 +151,8 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
|
|
||||||
// spawn new children with the contained so we only clear old content at the last moment.
|
// 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,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ namespace osu.Game.Overlays.Profile
|
|||||||
|
|
||||||
public abstract string Identifier { get; }
|
public abstract string Identifier { get; }
|
||||||
|
|
||||||
private readonly FillFlowContainer content;
|
private readonly FillFlowContainer<Drawable> content;
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
private readonly Box underscore;
|
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,
|
Direction = FillDirection.Vertical,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ namespace osu.Game.Overlays.Profile.Sections
|
|||||||
protected int VisiblePages;
|
protected int VisiblePages;
|
||||||
protected int ItemsPerPage;
|
protected int ItemsPerPage;
|
||||||
|
|
||||||
protected FillFlowContainer ItemsContainer { get; private set; }
|
protected ReverseChildIDFillFlowContainer<Drawable> ItemsContainer { get; private set; }
|
||||||
|
|
||||||
private APIRequest<List<TModel>> retrievalRequest;
|
private APIRequest<List<TModel>> retrievalRequest;
|
||||||
private CancellationTokenSource loadCancellation;
|
private CancellationTokenSource loadCancellation;
|
||||||
@ -48,11 +49,15 @@ namespace osu.Game.Overlays.Profile.Sections
|
|||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Children = new Drawable[]
|
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,
|
AutoSizeAxes = Axes.Y,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Spacing = new Vector2(0, 2),
|
Spacing = new Vector2(0, 2),
|
||||||
|
// ensure the container and its contents are in front of the "more" button.
|
||||||
|
Depth = float.MinValue
|
||||||
},
|
},
|
||||||
moreButton = new ShowMoreButton
|
moreButton = new ShowMoreButton
|
||||||
{
|
{
|
||||||
|
@ -135,7 +135,8 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new ScoresTable(1, response.Users),
|
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,
|
AutoSizeAxes = Axes.Y,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
@ -228,7 +228,10 @@ namespace osu.Game.Screens.Play
|
|||||||
onlineBeatmapRequest.Success += beatmapSet => Schedule(() =>
|
onlineBeatmapRequest.Success += beatmapSet => Schedule(() =>
|
||||||
{
|
{
|
||||||
this.beatmapSet = beatmapSet;
|
this.beatmapSet = beatmapSet;
|
||||||
beatmapPanelContainer.Child = new BeatmapCard(this.beatmapSet);
|
beatmapPanelContainer.Child = new BeatmapCard(this.beatmapSet)
|
||||||
|
{
|
||||||
|
Expanded = { Disabled = true }
|
||||||
|
};
|
||||||
checkForAutomaticDownload();
|
checkForAutomaticDownload();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user