1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-01 02:40:04 +08:00

Remove previous beatmap wedge implementation

This commit is contained in:
Salman Alshamrani
2025-03-20 11:41:05 -04:00
committed by Dean Herbert
Unverified
parent 244bae5d43
commit 430b22b383
5 changed files with 0 additions and 709 deletions
@@ -1,213 +0,0 @@
// 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 System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Screens.Select;
using osu.Game.Screens.SelectV2;
namespace osu.Game.Tests.Visual.SongSelectV2
{
public partial class TestSceneBeatmapInfoWedge : SongSelectComponentsTestScene
{
private RulesetStore rulesets = null!;
private TestBeatmapInfoWedgeV2 infoWedge = null!;
private readonly List<IBeatmap> beatmaps = new List<IBeatmap>();
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
}
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("reset mods", () => SelectedMods.SetDefault());
}
protected override void LoadComplete()
{
base.LoadComplete();
AddRange(new Drawable[]
{
// This exists only to make the wedge more visible in the test scene
new Box
{
Y = -20,
Colour = Colour4.Cornsilk.Darken(0.2f),
Height = BeatmapInfoWedgeV2.WEDGE_HEIGHT + 40,
Width = 0.65f,
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding { Top = 20, Left = -10 }
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 20 },
Child = infoWedge = new TestBeatmapInfoWedgeV2
{
Width = 0.6f,
RelativeSizeAxes = Axes.X,
},
}
});
AddSliderStep("change star difficulty", 0, 11.9, 5.55, v =>
{
foreach (var hasCurrentValue in infoWedge.ChildrenOfType<IHasCurrentValue<StarDifficulty>>())
hasCurrentValue.Current.Value = new StarDifficulty(v, 0);
});
}
[Test]
public void TestRulesetChange()
{
selectBeatmap(Beatmap.Value.Beatmap);
AddWaitStep("wait for select", 3);
foreach (var rulesetInfo in rulesets.AvailableRulesets)
{
var instance = rulesetInfo.CreateInstance();
var testBeatmap = createTestBeatmap(rulesetInfo);
beatmaps.Add(testBeatmap);
setRuleset(rulesetInfo);
selectBeatmap(testBeatmap);
testBeatmapLabels(instance);
}
}
[Test]
public void TestWedgeVisibility()
{
AddStep("hide", () => { infoWedge.Hide(); });
AddWaitStep("wait for hide", 3);
AddAssert("check visibility", () => infoWedge.Alpha == 0);
AddStep("show", () => { infoWedge.Show(); });
AddWaitStep("wait for show", 1);
AddAssert("check visibility", () => infoWedge.Alpha > 0);
}
private void testBeatmapLabels(Ruleset ruleset)
{
AddAssert("check title", () => infoWedge.Info!.TitleLabel.Current.Value == $"{ruleset.ShortName}Title");
AddAssert("check artist", () => infoWedge.Info!.ArtistLabel.Current.Value == $"{ruleset.ShortName}Artist");
}
[Test]
public void TestTruncation()
{
selectBeatmap(createLongMetadata());
}
[Test]
public void TestNullBeatmapWithBackground()
{
selectBeatmap(null);
AddAssert("check default title", () => infoWedge.Info!.TitleLabel.Current.Value == Beatmap.Default.BeatmapInfo.Metadata.Title);
AddAssert("check default artist", () => infoWedge.Info!.ArtistLabel.Current.Value == Beatmap.Default.BeatmapInfo.Metadata.Artist);
AddAssert("check no info labels", () => !infoWedge.Info.ChildrenOfType<BeatmapInfoWedge.WedgeInfoText.InfoLabel>().Any());
}
private void setRuleset(RulesetInfo rulesetInfo)
{
Container? containerBefore = null;
AddStep("set ruleset", () =>
{
// wedge content is only refreshed if the ruleset changes, so only wait for load in that case.
if (!rulesetInfo.Equals(Ruleset.Value))
containerBefore = infoWedge.DisplayedContent;
Ruleset.Value = rulesetInfo;
});
AddUntilStep("wait for async load", () => infoWedge.DisplayedContent != containerBefore);
}
private void selectBeatmap(IBeatmap? b)
{
Container? containerBefore = null;
AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () =>
{
containerBefore = infoWedge.DisplayedContent;
infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : CreateWorkingBeatmap(b);
infoWedge.Show();
});
AddUntilStep("wait for async load", () => infoWedge.DisplayedContent != containerBefore);
}
private IBeatmap createTestBeatmap(RulesetInfo ruleset)
{
List<HitObject> objects = new List<HitObject>();
for (double i = 0; i < 50000; i += 1000)
objects.Add(new TestHitObject { StartTime = i });
return new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
Metadata = new BeatmapMetadata
{
Author = { Username = $"{ruleset.ShortName}Author" },
Artist = $"{ruleset.ShortName}Artist",
Source = $"{ruleset.ShortName}Source",
Title = $"{ruleset.ShortName}Title"
},
Ruleset = ruleset,
StarRating = 6,
DifficultyName = $"{ruleset.ShortName}Version",
Difficulty = new BeatmapDifficulty()
},
HitObjects = objects
};
}
private IBeatmap createLongMetadata()
{
return new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
Metadata = new BeatmapMetadata
{
Author = { Username = "WWWWWWWWWWWWWWW" },
Artist = "Verrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Artist",
Source = "Verrrrry long Source",
Title = "Verrrrry long Title"
},
DifficultyName = "Verrrrrrrrrrrrrrrrrrrrrrrrrrrrry long Version",
Status = BeatmapOnlineStatus.Graveyard,
},
};
}
private partial class TestBeatmapInfoWedgeV2 : BeatmapInfoWedgeV2
{
public new Container? DisplayedContent => base.DisplayedContent;
public new WedgeInfoText? Info => base.Info;
}
private class TestHitObject : ConvertHitObject;
}
}
@@ -1,44 +0,0 @@
// 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 System.Linq;
using NUnit.Framework;
using osu.Framework.Localisation;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.SelectV2.Wedge;
namespace osu.Game.Tests.Visual.SongSelectV2
{
public partial class TestSceneDifficultyNameContent : SongSelectComponentsTestScene
{
private DifficultyNameContent? difficultyNameContent;
[Test]
public void TestLocalBeatmap()
{
AddStep("set component", () => Child = difficultyNameContent = new LocalDifficultyNameContent());
AddAssert("difficulty name is not set", () => LocalisableString.IsNullOrEmpty(difficultyNameContent.ChildrenOfType<TruncatingSpriteText>().Single().Text));
AddAssert("author is not set", () => LocalisableString.IsNullOrEmpty(difficultyNameContent.ChildrenOfType<OsuHoverContainer>().Single().ChildrenOfType<OsuSpriteText>().Single().Text));
AddStep("set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
DifficultyName = "really long difficulty name that gets truncated",
Metadata = new BeatmapMetadata
{
Author = { Username = "really long username that is autosized" },
},
OnlineID = 1,
}
}));
AddAssert("difficulty name is set", () => !LocalisableString.IsNullOrEmpty(difficultyNameContent.ChildrenOfType<TruncatingSpriteText>().Single().Text));
AddAssert("author is set", () => !LocalisableString.IsNullOrEmpty(difficultyNameContent.ChildrenOfType<OsuHoverContainer>().Single().ChildrenOfType<OsuSpriteText>().Single().Text));
}
}
}
@@ -1,330 +0,0 @@
// 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 System;
using System.Threading;
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.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osu.Game.Screens.Select;
using osuTK;
namespace osu.Game.Screens.SelectV2
{
public partial class BeatmapInfoWedgeV2 : VisibilityContainer
{
public const float WEDGE_HEIGHT = 120;
private const float shear_width = 21;
private const float transition_duration = 250;
private const float corner_radius = 10;
private const float colour_bar_width = 30;
/// Todo: move this const out to song select when more new design elements are implemented for the beatmap details area, since it applies to text alignment of various elements
private const float text_margin = 62;
private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / WEDGE_HEIGHT, 0);
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
[Resolved]
private OsuColour colours { get; set; } = null!;
[Resolved]
private BeatmapDifficultyCache difficultyCache { get; set; } = null!;
protected Container? DisplayedContent { get; private set; }
protected WedgeInfoText? Info { get; private set; }
private Container difficultyColourBar = null!;
private StarCounter starCounter = null!;
private StarRatingDisplay starRatingDisplay = null!;
private BeatmapSetOnlineStatusPill statusPill = null!;
private Container content = null!;
private IBindable<StarDifficulty?>? starDifficulty;
private CancellationTokenSource? cancellationSource;
public BeatmapInfoWedgeV2()
{
Height = WEDGE_HEIGHT;
Shear = wedged_container_shear;
Masking = true;
Margin = new MarginPadding { Left = -corner_radius };
EdgeEffect = new EdgeEffectParameters
{
Colour = Colour4.Black.Opacity(0.2f),
Type = EdgeEffectType.Shadow,
Radius = 3,
};
CornerRadius = corner_radius;
}
[BackgroundDependencyLoader]
private void load()
{
Child = content = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
// These elements can't be grouped with the rest of the content, due to being present either outside or under the backgrounds area
difficultyColourBar = new Container
{
Colour = Colour4.Transparent,
Depth = float.MaxValue,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
// By limiting the width we avoid this box showing up as an outline around the drawables that are on top of it.
Width = colour_bar_width + corner_radius,
Child = new Box { RelativeSizeAxes = Axes.Both }
},
new Container
{
// Applying the shear to this container and nesting the starCounter inside avoids
// the deformation that occurs if the shear is applied to the starCounter whilst rotated
Shear = -wedged_container_shear,
X = -colour_bar_width / 2,
Anchor = Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Width = colour_bar_width,
Child = starCounter = new StarCounter
{
Rotation = (float)(Math.Atan(shear_width / WEDGE_HEIGHT) * (180 / Math.PI)),
Colour = Colour4.Transparent,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(0.35f),
Direction = FillDirection.Vertical
}
},
new FillFlowContainer
{
Name = "Topright-aligned metadata",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Top = 3, Right = colour_bar_width + 8 },
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(0, 5),
Depth = float.MinValue,
Children = new Drawable[]
{
starRatingDisplay = new StarRatingDisplay(default, animated: true)
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Shear = -wedged_container_shear,
Alpha = 0,
},
statusPill = new BeatmapSetOnlineStatusPill
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Shear = -wedged_container_shear,
TextSize = 11,
TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 },
Alpha = 0,
}
}
},
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
ruleset.BindValueChanged(_ => updateDisplay());
starRatingDisplay.Current.BindValueChanged(s =>
{
// use actual stars as star counter has its own animation
starCounter.Current = (float)s.NewValue.Stars;
}, true);
starRatingDisplay.DisplayedStars.BindValueChanged(s =>
{
// sync color with star rating display
starCounter.Colour = s.NewValue >= 6.5 ? colours.Orange1 : Colour4.Black.Opacity(0.75f);
difficultyColourBar.FadeColour(colours.ForStarDifficulty(s.NewValue));
}, true);
}
private const double animation_duration = 600;
protected override void PopIn()
{
this.MoveToX(0, animation_duration, Easing.OutQuint);
this.FadeIn(200, Easing.In);
}
protected override void PopOut()
{
this.MoveToX(-150, animation_duration, Easing.OutQuint);
this.FadeOut(200, Easing.OutQuint);
}
private WorkingBeatmap beatmap = null!;
public WorkingBeatmap Beatmap
{
get => beatmap;
set
{
if (beatmap == value) return;
beatmap = value;
updateDisplay();
}
}
private Container? loadingInfo;
private void updateDisplay()
{
statusPill.Status = beatmap.BeatmapInfo.Status;
starDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo, (cancellationSource = new CancellationTokenSource()).Token);
starDifficulty.BindValueChanged(s =>
{
starRatingDisplay.Current.Value = s.NewValue ?? default;
starRatingDisplay.FadeIn(transition_duration);
});
Scheduler.AddOnce(() =>
{
LoadComponentAsync(loadingInfo = new Container
{
Padding = new MarginPadding { Right = colour_bar_width },
RelativeSizeAxes = Axes.Both,
Depth = DisplayedContent?.Depth + 1 ?? 0,
Child = new Container
{
Masking = true,
CornerRadius = corner_radius,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
// TODO: New wedge design uses a coloured horizontal gradient for its background, however this lacks implementation information in the figma draft.
// pending https://www.figma.com/file/DXKwqZhD5yyb1igc3mKo1P?node-id=2980:3361#340801912 being answered.
new BeatmapInfoWedgeBackground(beatmap) { Shear = -Shear },
Info = new WedgeInfoText(beatmap) { Shear = -Shear }
}
}
}, d =>
{
// Ensure we are the most recent loaded wedge.
if (d != loadingInfo) return;
removeOldInfo();
content.Add(DisplayedContent = d);
});
});
void removeOldInfo()
{
DisplayedContent?.FadeOut(transition_duration);
DisplayedContent?.Expire();
DisplayedContent = null;
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
cancellationSource?.Cancel();
}
public partial class WedgeInfoText : Container
{
public OsuSpriteText TitleLabel { get; private set; } = null!;
public OsuSpriteText ArtistLabel { get; private set; } = null!;
private readonly WorkingBeatmap working;
public WedgeInfoText(WorkingBeatmap working)
{
this.working = working;
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(SongSelect? songSelect, LocalisationManager localisation)
{
var metadata = working.Metadata;
var titleText = new RomanisableString(metadata.TitleUnicode, metadata.Title);
var artistText = new RomanisableString(metadata.ArtistUnicode, metadata.Artist);
Child = new FillFlowContainer
{
Name = "Top-left aligned metadata",
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Left = text_margin, Top = 12 },
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Children = new Drawable[]
{
new OsuHoverContainer
{
AutoSizeAxes = Axes.Both,
Action = () => songSelect?.Search(titleText.GetPreferred(localisation.CurrentParameters.Value.PreferOriginalScript)),
Child = TitleLabel = new TruncatingSpriteText
{
Shadow = true,
Text = titleText,
Font = OsuFont.TorusAlternate.With(size: 40, weight: FontWeight.SemiBold),
},
},
new OsuHoverContainer
{
AutoSizeAxes = Axes.Both,
Action = () => songSelect?.Search(artistText.GetPreferred(localisation.CurrentParameters.Value.PreferOriginalScript)),
Child = ArtistLabel = new TruncatingSpriteText
{
// TODO : figma design has a diffused shadow, instead of the solid one present here, not possible currently as far as i'm aware.
Shadow = true,
Text = artistText,
// Not sure if this should be semi bold or medium
Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold),
},
},
}
};
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
// best effort to confine the auto-sized text to wedge bounds
// the artist label doesn't have an extra text_margin as it doesn't touch the right metadata
TitleLabel.MaxWidth = DrawWidth - text_margin * 2 - shear_width;
ArtistLabel.MaxWidth = DrawWidth - text_margin - shear_width;
}
}
}
}
@@ -1,88 +0,0 @@
// 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 System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Localisation;
using osu.Game.Overlays;
namespace osu.Game.Screens.SelectV2.Wedge
{
public abstract partial class DifficultyNameContent : CompositeDrawable
{
protected OsuSpriteText DifficultyName = null!;
private OsuSpriteText mappedByLabel = null!;
protected OsuHoverContainer MapperLink = null!;
protected OsuSpriteText MapperName = null!;
protected DifficultyNameContent()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
DifficultyName = new TruncatingSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(weight: FontWeight.SemiBold),
},
mappedByLabel = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
// TODO: better null display? beatmap carousel panels also just show this text currently.
Text = " mapped by ",
Font = OsuFont.GetFont(size: 14),
},
// This is not a `LinkFlowContainer` as there are single-frame layout issues when Update()
// is being used for layout, see https://github.com/ppy/osu-framework/issues/3369.
MapperLink = new MapperLinkContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Child = MapperName = new OsuSpriteText
{
Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 14),
}
},
}
};
}
protected override void Update()
{
base.Update();
// truncate difficulty name when width exceeds bounds, prioritizing mapper name display
DifficultyName.MaxWidth = Math.Max(DrawWidth - mappedByLabel.DrawWidth
- MapperName.DrawWidth, 0);
}
private partial class MapperLinkContainer : OsuHoverContainer
{
[BackgroundDependencyLoader]
private void load(OverlayColourProvider? overlayColourProvider, OsuColour colours)
{
TooltipText = ContextMenuStrings.ViewProfile;
IdleColour = overlayColourProvider?.Light2 ?? colours.Blue;
}
}
}
}
@@ -1,34 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Online;
using osu.Game.Online.Chat;
namespace osu.Game.Screens.SelectV2.Wedge
{
public partial class LocalDifficultyNameContent : DifficultyNameContent
{
[Resolved]
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
[Resolved]
private ILinkHandler? linkHandler { get; set; }
protected override void LoadComplete()
{
base.LoadComplete();
beatmap.BindValueChanged(b =>
{
DifficultyName.Text = b.NewValue.BeatmapInfo.DifficultyName;
// TODO: should be the mapper of the guest difficulty, but that isn't stored correctly yet (see https://github.com/ppy/osu/issues/12965)
MapperName.Text = b.NewValue.Metadata.Author.Username;
MapperLink.Action = () => linkHandler?.HandleLink(new LinkDetails(LinkAction.OpenUserProfile, b.NewValue.Metadata.Author));
}, true);
}
}
}