From a07f8c74dc454de38c1ca4b54323e29345747bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Dec 2021 15:04:44 +0100 Subject: [PATCH 01/10] Add basic structure for composable card dropdown --- .../Visual/Beatmaps/TestSceneBeatmapCard.cs | 10 +- .../Beatmaps/Drawables/Cards/BeatmapCard.cs | 413 +++++++++--------- .../Drawables/Cards/BeatmapCardDropdown.cs | 85 ++++ 3 files changed, 307 insertions(+), 201 deletions(-) create mode 100644 osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs index 5effc1f215..d3ce5028b7 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs @@ -12,6 +12,7 @@ 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 +228,7 @@ namespace osu.Game.Tests.Visual.Beatmaps new BasicScrollContainer { RelativeSizeAxes = Axes.Both, - Child = new FillFlowContainer + Child = new ReverseChildIDFillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -248,6 +249,11 @@ 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 => this.ChildrenOfType().Last().Expanded.Value = expanded); + } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index 37c1bacda4..46ee9afb86 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -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 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; @@ -73,242 +75,255 @@ 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 = new BeatmapCardDropdown(height) { - downloadTracker, - rightAreaBackground = new Container + Body = 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 - { - 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 { - 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 + 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 + { + 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[] + { + 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) + } + } + } + } + }, + downloadProgressBar = new BeatmapCardDownloadProgressBar + { + RelativeSizeAxes = Axes.X, + Height = 6, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + State = { BindTarget = downloadTracker.State }, + Progress = { BindTarget = downloadTracker.Progress } + } + } } } } } - } + }, + Dropdown = 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) @@ -388,7 +403,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards { float targetWidth = width - height; if (IsHovered) - targetWidth = targetWidth - icon_area_width + corner_radius; + targetWidth = targetWidth - icon_area_width + CORNER_RADIUS; thumbnail.Dimmed.Value = IsHovered; diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs new file mode 100644 index 0000000000..29f12de947 --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs @@ -0,0 +1,85 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Overlays; + +namespace osu.Game.Beatmaps.Drawables.Cards +{ + public class BeatmapCardDropdown : CompositeDrawable + { + public Drawable Body + { + set => bodyContent.Child = value; + } + + public Drawable Dropdown + { + set => dropdownContent.Child = value; + } + + public Bindable Expanded { get; } = new BindableBool(); + + private readonly Box background; + private readonly Container bodyContent; + private readonly Container dropdownContent; + + public BeatmapCardDropdown(float height) + { + RelativeSizeAxes = Axes.X; + Height = height; + + InternalChild = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + CornerRadius = BeatmapCard.CORNER_RADIUS, + Masking = true, + + 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 Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = height }, + Alpha = 0 + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + background.Colour = colourProvider.Background2; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Expanded.BindValueChanged(_ => updateState()); + } + + 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); + } + } +} From 3fea8d5e62e709ce46bc480e32dc10b33c6c251f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Dec 2021 15:48:02 +0100 Subject: [PATCH 02/10] Implement visual behaviour of expanded card state --- .../Beatmaps/Drawables/Cards/BeatmapCard.cs | 15 +++++---- .../Drawables/Cards/BeatmapCardDropdown.cs | 33 +++++++++++++++++-- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index 46ee9afb86..f0425794be 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -359,7 +359,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards { base.LoadComplete(); - downloadTracker.State.BindValueChanged(_ => updateState(), true); + downloadTracker.State.BindValueChanged(_ => updateState()); + Expanded.BindValueChanged(_ => updateState(), true); FinishTransforms(true); } @@ -401,19 +402,21 @@ namespace osu.Game.Beatmaps.Drawables.Cards private void updateState() { + bool showDetails = IsHovered || Expanded.Value; + float targetWidth = width - height; - if (IsHovered) + if (showDetails) targetWidth = targetWidth - icon_area_width + CORNER_RADIUS; - thumbnail.Dimmed.Value = IsHovered; + 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) { diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs index 29f12de947..877338ecb0 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs @@ -5,8 +5,10 @@ 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.Game.Overlays; +using osuTK; namespace osu.Game.Beatmaps.Drawables.Cards { @@ -25,15 +27,17 @@ namespace osu.Game.Beatmaps.Drawables.Cards public Bindable Expanded { get; } = new BindableBool(); private readonly Box background; + private readonly Container content; private readonly Container bodyContent; private readonly Container dropdownContent; + private readonly Container borderContainer; public BeatmapCardDropdown(float height) { RelativeSizeAxes = Axes.X; Height = height; - InternalChild = new Container + InternalChild = content = new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -59,6 +63,19 @@ namespace osu.Game.Beatmaps.Drawables.Cards AutoSizeAxes = Axes.Y, Margin = new MarginPadding { Top = height }, Alpha = 0 + }, + borderContainer = new Container + { + RelativeSizeAxes = Axes.Both, + CornerRadius = BeatmapCard.CORNER_RADIUS, + Masking = true, + BorderThickness = 3, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } } } }; @@ -68,18 +85,30 @@ namespace osu.Game.Beatmaps.Drawables.Cards private void load(OverlayColourProvider colourProvider) { background.Colour = colourProvider.Background2; + borderContainer.BorderColour = colourProvider.Highlight1; } protected override void LoadComplete() { base.LoadComplete(); - Expanded.BindValueChanged(_ => updateState()); + Expanded.BindValueChanged(_ => updateState(), true); + FinishTransforms(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); } } } From 250e5b47b7b1f9e7c50c0f552529f9b0bc47b4a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 28 Nov 2021 20:25:23 +0100 Subject: [PATCH 03/10] Move "extra info" beatmap card row to separate component --- .../Beatmaps/Drawables/Cards/BeatmapCard.cs | 24 +---------- .../Cards/BeatmapCardExtraInfoRow.cs | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index f0425794be..1a05607074 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -276,29 +276,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards 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 - { - 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 BeatmapCardExtraInfoRow(beatmapSet) } }, downloadProgressBar = new BeatmapCardDownloadProgressBar diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs new file mode 100644 index 0000000000..c64e5b83d8 --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs @@ -0,0 +1,43 @@ +// Copyright (c) ppy Pty Ltd . 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 : CompositeDrawable + { + public BeatmapCardExtraInfoRow(APIBeatmapSet beatmapSet) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + InternalChild = 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) + } + } + }; + } + } +} From e451e43b9022542bc28c6f96e2263c44b27a1213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Dec 2021 16:31:45 +0100 Subject: [PATCH 04/10] Implement input handling behaviour of beatmap card dropdown --- .../Beatmaps/Drawables/Cards/BeatmapCard.cs | 12 ++++- .../Drawables/Cards/BeatmapCardDropdown.cs | 52 +++++++++++++++++-- .../Cards/BeatmapCardExtraInfoRow.cs | 4 +- .../Drawables/Cards/HoverHandlingContainer.cs | 27 ++++++++++ 4 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index 1a05607074..2ab77539b3 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -44,6 +44,8 @@ namespace osu.Game.Beatmaps.Drawables.Cards private readonly BeatmapDownloadTracker downloadTracker; + private BeatmapCardDropdown dropdown = null!; + private BeatmapCardThumbnail thumbnail = null!; private Container rightAreaBackground = null!; @@ -80,7 +82,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards GridContainer titleContainer; GridContainer artistContainer; - InternalChild = new BeatmapCardDropdown(height) + InternalChild = dropdown = new BeatmapCardDropdown(height) { Body = new Container { @@ -277,6 +279,14 @@ namespace osu.Game.Beatmaps.Drawables.Cards ChildrenEnumerable = createStatistics() }, new BeatmapCardExtraInfoRow(beatmapSet) + { + Hovered = _ => + { + dropdown.ScheduleShow(); + return false; + }, + Unhovered = _ => dropdown.ScheduleHide() + } } }, downloadProgressBar = new BeatmapCardDownloadProgressBar diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs index 877338ecb0..ef4ba67ab7 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs @@ -1,12 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + 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.Threading; using osu.Game.Overlays; using osuTK; @@ -37,13 +40,13 @@ namespace osu.Game.Beatmaps.Drawables.Cards RelativeSizeAxes = Axes.X; Height = height; - InternalChild = content = new Container + InternalChild = content = new HoverHandlingContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, CornerRadius = BeatmapCard.CORNER_RADIUS, Masking = true, - + Unhovered = _ => checkForHide(), Children = new Drawable[] { background = new Box @@ -57,12 +60,18 @@ namespace osu.Game.Beatmaps.Drawables.Cards CornerRadius = BeatmapCard.CORNER_RADIUS, Masking = true, }, - dropdownContent = new Container + dropdownContent = new HoverHandlingContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Margin = new MarginPadding { Top = height }, - Alpha = 0 + Alpha = 0, + Hovered = _ => + { + keep(); + return true; + }, + Unhovered = _ => checkForHide() }, borderContainer = new Container { @@ -95,6 +104,41 @@ namespace osu.Game.Beatmaps.Drawables.Cards FinishTransforms(true); } + private ScheduledDelegate? scheduledExpandedChange; + + public void ScheduleShow() + { + scheduledExpandedChange?.Cancel(); + if (Expanded.Value) + return; + + scheduledExpandedChange = Scheduler.AddDelayed(() => Expanded.Value = true, 100); + } + + public void ScheduleHide() + { + scheduledExpandedChange?.Cancel(); + if (!Expanded.Value) + return; + + scheduledExpandedChange = Scheduler.AddDelayed(() => Expanded.Value = false, 500); + } + + private void checkForHide() + { + if (content.IsHovered || dropdownContent.IsHovered) + return; + + scheduledExpandedChange?.Cancel(); + Expanded.Value = false; + } + + private void keep() + { + scheduledExpandedChange?.Cancel(); + Expanded.Value = true; + } + private void updateState() { background.FadeTo(Expanded.Value ? 1 : 0, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs index c64e5b83d8..0a9d98e621 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardExtraInfoRow.cs @@ -8,14 +8,14 @@ using osuTK; namespace osu.Game.Beatmaps.Drawables.Cards { - public class BeatmapCardExtraInfoRow : CompositeDrawable + public class BeatmapCardExtraInfoRow : HoverHandlingContainer { public BeatmapCardExtraInfoRow(APIBeatmapSet beatmapSet) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - InternalChild = new FillFlowContainer + Child = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs b/osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs new file mode 100644 index 0000000000..1e2c616332 --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs @@ -0,0 +1,27 @@ +// Copyright (c) ppy Pty Ltd . 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? Hovered { get; set; } + public Action? Unhovered { get; set; } + + protected override bool OnHover(HoverEvent e) => Hovered?.Invoke(e) ?? base.OnHover(e); + + protected override void OnHoverLost(HoverLostEvent e) + { + if (Unhovered != null) + Unhovered?.Invoke(e); + else + base.OnHoverLost(e); + } + } +} From af10223ac4d7841cd5fc7212a09d5498211fbc6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 29 Nov 2021 01:08:45 +0100 Subject: [PATCH 05/10] Add reverse fill flows & depth specs at usage sites for correct Z-ordering --- osu.Game/Overlays/BeatmapListingOverlay.cs | 2 +- osu.Game/Overlays/Profile/ProfileSection.cs | 5 +++-- .../Overlays/Profile/Sections/PaginatedProfileSubsection.cs | 6 ++++-- osu.Game/Overlays/Rankings/SpotlightsLayout.cs | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 49f2f5c211..a454af00c4 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -151,7 +151,7 @@ namespace osu.Game.Overlays } // spawn new children with the contained so we only clear old content at the last moment. - var content = new FillFlowContainer + var content = new ReverseChildIDFillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/Profile/ProfileSection.cs b/osu.Game/Overlays/Profile/ProfileSection.cs index 6223b32814..fdf3077bf0 100644 --- a/osu.Game/Overlays/Profile/ProfileSection.cs +++ b/osu.Game/Overlays/Profile/ProfileSection.cs @@ -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 content; private readonly Box background; private readonly Box underscore; @@ -79,7 +80,7 @@ namespace osu.Game.Overlays.Profile } } }, - content = new FillFlowContainer + content = new ReverseChildIDFillFlowContainer { Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index 130ae44273..e7053ec4fa 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -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 ItemsContainer { get; private set; } private APIRequest> retrievalRequest; private CancellationTokenSource loadCancellation; @@ -48,11 +49,12 @@ namespace osu.Game.Overlays.Profile.Sections Direction = FillDirection.Vertical, Children = new Drawable[] { - ItemsContainer = new FillFlowContainer + ItemsContainer = new ReverseChildIDFillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Spacing = new Vector2(0, 2), + Depth = float.MinValue }, moreButton = new ShowMoreButton { diff --git a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs index a37f762532..61d68b7d29 100644 --- a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs +++ b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs @@ -135,7 +135,7 @@ namespace osu.Game.Overlays.Rankings Children = new Drawable[] { new ScoresTable(1, response.Users), - new FillFlowContainer + new ReverseChildIDFillFlowContainer { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, From af35652b8b9f97dc700f19040e161f8d477c2748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Dec 2021 17:16:41 +0100 Subject: [PATCH 06/10] Disable beatmap card expansion on solo spectator screen --- .../Visual/Beatmaps/TestSceneBeatmapCard.cs | 9 +++++++- .../Drawables/Cards/BeatmapCardDropdown.cs | 22 +++++++++++++++---- osu.Game/Screens/Play/SoloSpectator.cs | 5 ++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs index d3ce5028b7..f835d21603 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapCard.cs @@ -6,6 +6,7 @@ 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; @@ -253,7 +254,13 @@ namespace osu.Game.Tests.Visual.Beatmaps { createTestCase(beatmapSetInfo => new BeatmapCard(beatmapSetInfo)); - AddToggleStep("toggle expanded state", expanded => this.ChildrenOfType().Last().Expanded.Value = expanded); + AddToggleStep("toggle expanded state", expanded => + { + var card = this.ChildrenOfType().Last(); + if (!card.Expanded.Disabled) + card.Expanded.Value = expanded; + }); + AddToggleStep("disable/enable expansion", disabled => this.ChildrenOfType().ForEach(card => card.Expanded.Disabled = disabled)); } } } diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs index ef4ba67ab7..366e5cb0fc 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs @@ -109,23 +109,34 @@ namespace osu.Game.Beatmaps.Drawables.Cards public void ScheduleShow() { scheduledExpandedChange?.Cancel(); - if (Expanded.Value) + if (Expanded.Disabled || Expanded.Value) return; - scheduledExpandedChange = Scheduler.AddDelayed(() => Expanded.Value = true, 100); + scheduledExpandedChange = Scheduler.AddDelayed(() => + { + if (!Expanded.Disabled) + Expanded.Value = true; + }, 100); } public void ScheduleHide() { scheduledExpandedChange?.Cancel(); - if (!Expanded.Value) + if (Expanded.Disabled || !Expanded.Value) return; - scheduledExpandedChange = Scheduler.AddDelayed(() => Expanded.Value = false, 500); + scheduledExpandedChange = Scheduler.AddDelayed(() => + { + if (!Expanded.Disabled) + Expanded.Value = false; + }, 500); } private void checkForHide() { + if (Expanded.Disabled) + return; + if (content.IsHovered || dropdownContent.IsHovered) return; @@ -135,6 +146,9 @@ namespace osu.Game.Beatmaps.Drawables.Cards private void keep() { + if (Expanded.Disabled) + return; + scheduledExpandedChange?.Cancel(); Expanded.Value = true; } diff --git a/osu.Game/Screens/Play/SoloSpectator.cs b/osu.Game/Screens/Play/SoloSpectator.cs index 7fea44b3ea..3918dbe8fc 100644 --- a/osu.Game/Screens/Play/SoloSpectator.cs +++ b/osu.Game/Screens/Play/SoloSpectator.cs @@ -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(); }); From 0f743893890029d010b75a42d6472c79a12b69c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 5 Dec 2021 17:35:10 +0100 Subject: [PATCH 07/10] Add scrolling for long difficulty lists in beatmap card --- .../Drawables/Cards/BeatmapCardDropdown.cs | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs index 366e5cb0fc..a24f09a9b5 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs @@ -3,13 +3,17 @@ #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; @@ -24,7 +28,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards public Drawable Dropdown { - set => dropdownContent.Child = value; + set => dropdownScroll.Child = value; } public Bindable Expanded { get; } = new BindableBool(); @@ -33,6 +37,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards private readonly Container content; private readonly Container bodyContent; private readonly Container dropdownContent; + private readonly OsuScrollContainer dropdownScroll; private readonly Container borderContainer; public BeatmapCardDropdown(float height) @@ -71,7 +76,12 @@ namespace osu.Game.Beatmaps.Drawables.Cards keep(); return true; }, - Unhovered = _ => checkForHide() + Unhovered = _ => checkForHide(), + Child = dropdownScroll = new DropdownScrollContainer + { + RelativeSizeAxes = Axes.X, + ScrollbarVisible = false + } }, borderContainer = new Container { @@ -168,5 +178,54 @@ namespace osu.Game.Beatmaps.Drawables.Cards Hollow = true, }, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); } + + private class DropdownScrollContainer : OsuScrollContainer + { + public DropdownScrollContainer() + { + 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); + } + } } } From ccfc361626415fe5993fae093bcfb35abcf980e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Dec 2021 20:49:29 +0100 Subject: [PATCH 08/10] Apply naming suggestions --- osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs | 12 ++++++------ ...eatmapCardDropdown.cs => BeatmapCardContent.cs} | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) rename osu.Game/Beatmaps/Drawables/Cards/{BeatmapCardDropdown.cs => BeatmapCardContent.cs} (94%) diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs index 2ab77539b3..9031d6df1a 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCard.cs @@ -44,7 +44,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards private readonly BeatmapDownloadTracker downloadTracker; - private BeatmapCardDropdown dropdown = null!; + private BeatmapCardContent content = null!; private BeatmapCardThumbnail thumbnail = null!; @@ -82,9 +82,9 @@ namespace osu.Game.Beatmaps.Drawables.Cards GridContainer titleContainer; GridContainer artistContainer; - InternalChild = dropdown = new BeatmapCardDropdown(height) + InternalChild = content = new BeatmapCardContent(height) { - Body = new Container + MainContent = new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] @@ -282,10 +282,10 @@ namespace osu.Game.Beatmaps.Drawables.Cards { Hovered = _ => { - dropdown.ScheduleShow(); + content.ScheduleShow(); return false; }, - Unhovered = _ => dropdown.ScheduleHide() + Unhovered = _ => content.ScheduleHide() } } }, @@ -304,7 +304,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards } } }, - Dropdown = new Container + ExpandedContent = new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs similarity index 94% rename from osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs rename to osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs index a24f09a9b5..7f94d0e3b7 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardDropdown.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/BeatmapCardContent.cs @@ -19,14 +19,14 @@ using osuTK; namespace osu.Game.Beatmaps.Drawables.Cards { - public class BeatmapCardDropdown : CompositeDrawable + public class BeatmapCardContent : CompositeDrawable { - public Drawable Body + public Drawable MainContent { set => bodyContent.Child = value; } - public Drawable Dropdown + public Drawable ExpandedContent { set => dropdownScroll.Child = value; } @@ -40,7 +40,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards private readonly OsuScrollContainer dropdownScroll; private readonly Container borderContainer; - public BeatmapCardDropdown(float height) + public BeatmapCardContent(float height) { RelativeSizeAxes = Axes.X; Height = height; @@ -77,7 +77,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards return true; }, Unhovered = _ => checkForHide(), - Child = dropdownScroll = new DropdownScrollContainer + Child = dropdownScroll = new ExpandedContentScrollContainer { RelativeSizeAxes = Axes.X, ScrollbarVisible = false @@ -179,9 +179,9 @@ namespace osu.Game.Beatmaps.Drawables.Cards }, BeatmapCard.TRANSITION_DURATION, Easing.OutQuint); } - private class DropdownScrollContainer : OsuScrollContainer + private class ExpandedContentScrollContainer : OsuScrollContainer { - public DropdownScrollContainer() + public ExpandedContentScrollContainer() { ScrollbarVisible = false; } From 82ed8eae6bfa4f42ecb274f1138f414ad42f7d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Dec 2021 20:52:06 +0100 Subject: [PATCH 09/10] Ensure hover handling container always calls base on hover events --- .../Drawables/Cards/HoverHandlingContainer.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs b/osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs index 1e2c616332..fe37616755 100644 --- a/osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs +++ b/osu.Game/Beatmaps/Drawables/Cards/HoverHandlingContainer.cs @@ -14,14 +14,16 @@ namespace osu.Game.Beatmaps.Drawables.Cards public Func? Hovered { get; set; } public Action? Unhovered { get; set; } - protected override bool OnHover(HoverEvent e) => Hovered?.Invoke(e) ?? base.OnHover(e); + protected override bool OnHover(HoverEvent e) + { + bool handledByBase = base.OnHover(e); + return Hovered?.Invoke(e) ?? handledByBase; + } protected override void OnHoverLost(HoverLostEvent e) { - if (Unhovered != null) - Unhovered?.Invoke(e); - else - base.OnHoverLost(e); + base.OnHoverLost(e); + Unhovered?.Invoke(e); } } } From 999bba439f98bb31741acbfadd4b7a834be3a7a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 6 Dec 2021 21:00:26 +0100 Subject: [PATCH 10/10] Clarify usages of reverse child ID flow with inline comments --- osu.Game/Overlays/BeatmapListingOverlay.cs | 1 + osu.Game/Overlays/Profile/ProfileSection.cs | 2 ++ .../Overlays/Profile/Sections/PaginatedProfileSubsection.cs | 3 +++ osu.Game/Overlays/Rankings/SpotlightsLayout.cs | 1 + 4 files changed, 7 insertions(+) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index a454af00c4..6b27dbf847 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -151,6 +151,7 @@ namespace osu.Game.Overlays } // spawn new children with the contained so we only clear old content at the last moment. + // reverse ID flow is required for correct Z-ordering of the cards' expandable content (last card should be front-most). var content = new ReverseChildIDFillFlowContainer { RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/Profile/ProfileSection.cs b/osu.Game/Overlays/Profile/ProfileSection.cs index fdf3077bf0..fc6fce0d8e 100644 --- a/osu.Game/Overlays/Profile/ProfileSection.cs +++ b/osu.Game/Overlays/Profile/ProfileSection.cs @@ -80,6 +80,8 @@ namespace osu.Game.Overlays.Profile } } }, + // 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 { Direction = FillDirection.Vertical, diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs index e7053ec4fa..9dcbf6142d 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedProfileSubsection.cs @@ -49,11 +49,14 @@ namespace osu.Game.Overlays.Profile.Sections Direction = FillDirection.Vertical, Children = new Drawable[] { + // 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 { 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 diff --git a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs index 61d68b7d29..bcfc2499b9 100644 --- a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs +++ b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs @@ -135,6 +135,7 @@ namespace osu.Game.Overlays.Rankings Children = new Drawable[] { new ScoresTable(1, response.Users), + // reverse ID flow is required for correct Z-ordering of the cards' expandable content (last card should be front-most). new ReverseChildIDFillFlowContainer { AutoSizeAxes = Axes.Y,