Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement designs for beatmap carousel v2 #31774

Merged
merged 31 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2d75030
Change default carousel item header to 50px
frenzibyte Feb 5, 2025
f2d259c
Cache overlay colour provider to carousel tests
frenzibyte Feb 5, 2025
a5fa04e
Extend beatmap carousel width in tests
frenzibyte Feb 5, 2025
206b5c9
Implement beatmap set header design
frenzibyte Feb 5, 2025
04d8baf
Implement beatmap difficulty panel design
frenzibyte Feb 5, 2025
696366f
Implement beatmap "standalone" panel design
frenzibyte Feb 5, 2025
c94d11b
Add beatmap carousel to new song select screen
frenzibyte Feb 5, 2025
29882a2
Allow importing real beatmaps in song select test scene
frenzibyte Feb 5, 2025
f9962f9
Implement group panel design
frenzibyte Feb 5, 2025
04a3ee8
Fix design tests
frenzibyte Feb 5, 2025
467ea91
Fix basic code quality issues
frenzibyte Feb 6, 2025
72a62b7
Simplify some code
frenzibyte Feb 6, 2025
5e894a6
Fix carousel tests failing due to X offsets
frenzibyte Feb 6, 2025
aab4a79
Push all beatmap panels to hide their tails
frenzibyte Feb 6, 2025
ecc3aea
Make `BeatmapPanel` appear hovered on keyboard selection even if sele…
frenzibyte Feb 6, 2025
134e62c
Abstractify beatmap panel piece and update all panel implementations
frenzibyte Feb 6, 2025
3ab208b
Fix group visual test scene
frenzibyte Feb 6, 2025
e1d6ce5
Add V2 suffix for easier test browsing
frenzibyte Feb 6, 2025
78cd093
Fix broken input handling with structural changes
frenzibyte Feb 6, 2025
aa9727c
Fix helper method in carousel test scene
frenzibyte Feb 6, 2025
05a9160
Simplify LINQ expressions to appease CI
frenzibyte Feb 6, 2025
d8f3dbf
Merge branch 'master' into carousel-v2-implement-designs
peppy Feb 7, 2025
3a04642
Remove unnecessary V2 suffixes
peppy Feb 12, 2025
151101b
Mark `Action` as `init` only
peppy Feb 12, 2025
5548847
Rename classes for better discoverability / grouping
peppy Feb 12, 2025
40cae4f
Merge branch 'master' into carousel-v2-implement-designs
peppy Feb 18, 2025
88ec204
User inheritance to avoid `Piece` structural nightmare
peppy Feb 18, 2025
5de9584
Move `PanelXOffset` to `init` property rather than ctor
peppy Feb 18, 2025
644fb29
Fix input handling not matching latest `master` logic
peppy Feb 18, 2025
7e19844
Tidy up remaining common code
peppy Feb 18, 2025
092d80c
Fix `PanelBeatmapStandalone` not handling selection state
frenzibyte Feb 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Screens.Select;
using osu.Game.Screens.SelectV2;
using osu.Game.Tests.Beatmaps;
Expand All @@ -39,6 +40,9 @@ public abstract partial class BeatmapCarouselV2TestScene : OsuManualInputManager
[Cached(typeof(BeatmapStore))]
private BeatmapStore store;

[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);

private OsuTextFlowContainer stats = null!;

private int beatmapCount;
Expand Down Expand Up @@ -94,7 +98,7 @@ protected void CreateCarousel()
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 500,
Width = 800,
RelativeSizeAxes = Axes.Y,
},
},
Expand Down Expand Up @@ -185,6 +189,7 @@ protected void ClickVisiblePanel<T>(int index)
.Where(p => ((ICarouselPanel)p).Item?.IsVisible == true)
.OrderBy(p => p.Y)
.ElementAt(index)
.ChildrenOfType<PanelBase>().Single()
.TriggerClick();
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,42 +28,42 @@ public void SetUpSteps()
[Test]
public void TestOpenCloseGroupWithNoSelectionMouse()
{
AddAssert("no beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("no sets visible", () => Carousel.ChildrenOfType<BeatmapSetPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddAssert("no beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("no sets visible", () => Carousel.ChildrenOfType<PanelBeatmapSet>().Count(p => p.Alpha > 0), () => Is.Zero);
CheckNoSelection();

ClickVisiblePanel<GroupPanel>(0);
ClickVisiblePanel<PanelGroup>(0);

AddUntilStep("some sets visible", () => Carousel.ChildrenOfType<BeatmapSetPanel>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("some sets visible", () => Carousel.ChildrenOfType<PanelBeatmapSet>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.Zero);
CheckNoSelection();

ClickVisiblePanel<GroupPanel>(0);
ClickVisiblePanel<PanelGroup>(0);

AddUntilStep("no sets visible", () => Carousel.ChildrenOfType<BeatmapSetPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("no sets visible", () => Carousel.ChildrenOfType<PanelBeatmapSet>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.Zero);
CheckNoSelection();
}

[Test]
public void TestOpenCloseGroupWithNoSelectionKeyboard()
{
AddAssert("no beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("no sets visible", () => Carousel.ChildrenOfType<BeatmapSetPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddAssert("no beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("no sets visible", () => Carousel.ChildrenOfType<PanelBeatmapSet>().Count(p => p.Alpha > 0), () => Is.Zero);
CheckNoSelection();

SelectNextPanel();
Select();

AddUntilStep("some sets visible", () => Carousel.ChildrenOfType<BeatmapSetPanel>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("some sets visible", () => Carousel.ChildrenOfType<PanelBeatmapSet>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.Zero);
AddAssert("keyboard selected is expanded", () => GetKeyboardSelectedPanel()?.Expanded.Value, () => Is.True);
CheckNoSelection();

Select();

AddUntilStep("no sets visible", () => Carousel.ChildrenOfType<BeatmapSetPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("no sets visible", () => Carousel.ChildrenOfType<PanelBeatmapSet>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.Zero);
AddAssert("keyboard selected is collapsed", () => GetKeyboardSelectedPanel()?.Expanded.Value, () => Is.False);
CheckNoSelection();
}
Expand Down Expand Up @@ -96,10 +96,10 @@ public void TestCarouselRemembersSelection()
AddUntilStep("drawable selection restored", () => GetSelectedPanel()?.Item?.Model, () => Is.EqualTo(selection));
AddAssert("carousel item is visible", () => GetSelectedPanel()?.Item?.IsVisible, () => Is.True);

ClickVisiblePanel<GroupPanel>(0);
ClickVisiblePanel<PanelGroup>(0);
AddUntilStep("carousel item not visible", GetSelectedPanel, () => Is.Null);

ClickVisiblePanel<GroupPanel>(0);
ClickVisiblePanel<PanelGroup>(0);
AddUntilStep("carousel item is visible", () => GetSelectedPanel()?.Item?.IsVisible, () => Is.True);
}

Expand Down Expand Up @@ -137,7 +137,7 @@ public void TestKeyboardSelection()
// open first group
Select();
CheckNoSelection();
AddUntilStep("some beatmaps visible", () => Carousel.ChildrenOfType<BeatmapSetPanel>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));
AddUntilStep("some beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmapSet>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));

SelectNextPanel();
Select();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,32 @@ public void SetUpSteps()
[Test]
public void TestOpenCloseGroupWithNoSelectionMouse()
{
AddAssert("no beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddAssert("no beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.Zero);
CheckNoSelection();

ClickVisiblePanel<GroupPanel>(0);
AddUntilStep("some beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));
ClickVisiblePanel<PanelGroup>(0);
AddUntilStep("some beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));
CheckNoSelection();

ClickVisiblePanel<GroupPanel>(0);
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
ClickVisiblePanel<PanelGroup>(0);
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.Zero);
CheckNoSelection();
}

[Test]
public void TestOpenCloseGroupWithNoSelectionKeyboard()
{
AddAssert("no beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddAssert("no beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.Zero);
CheckNoSelection();

SelectNextPanel();
Select();
AddUntilStep("some beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));
AddUntilStep("some beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));
AddAssert("keyboard selected is expanded", () => GetKeyboardSelectedPanel()?.Expanded.Value, () => Is.True);
CheckNoSelection();

Select();
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.Zero);
AddUntilStep("no beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.Zero);
AddAssert("keyboard selected is collapsed", () => GetKeyboardSelectedPanel()?.Expanded.Value, () => Is.False);
CheckNoSelection();
}
Expand Down Expand Up @@ -87,10 +87,10 @@ public void TestCarouselRemembersSelection()
AddUntilStep("drawable selection restored", () => GetSelectedPanel()?.Item?.Model, () => Is.EqualTo(selection));
AddAssert("carousel item is visible", () => GetSelectedPanel()?.Item?.IsVisible, () => Is.True);

ClickVisiblePanel<GroupPanel>(0);
ClickVisiblePanel<PanelGroup>(0);
AddUntilStep("carousel item not visible", GetSelectedPanel, () => Is.Null);

ClickVisiblePanel<GroupPanel>(0);
ClickVisiblePanel<PanelGroup>(0);
AddUntilStep("carousel item is visible", () => GetSelectedPanel()?.Item?.IsVisible, () => Is.True);
}

Expand Down Expand Up @@ -120,18 +120,18 @@ public void TestGroupSelectionOnHeaderMouse()
SelectNextGroup();
WaitForGroupSelection(0, 0);

AddAssert("keyboard selected panel is beatmap", GetKeyboardSelectedPanel, Is.TypeOf<BeatmapPanel>);
AddAssert("selected panel is beatmap", GetSelectedPanel, Is.TypeOf<BeatmapPanel>);
AddAssert("keyboard selected panel is beatmap", GetKeyboardSelectedPanel, Is.TypeOf<PanelBeatmap>);
AddAssert("selected panel is beatmap", GetSelectedPanel, Is.TypeOf<PanelBeatmap>);

ClickVisiblePanel<GroupPanel>(0);
AddAssert("keyboard selected panel is group", GetKeyboardSelectedPanel, Is.TypeOf<GroupPanel>);
ClickVisiblePanel<PanelGroup>(0);
AddAssert("keyboard selected panel is group", GetKeyboardSelectedPanel, Is.TypeOf<PanelGroup>);
AddAssert("keyboard selected panel is contracted", () => GetKeyboardSelectedPanel()?.Expanded.Value, () => Is.False);

ClickVisiblePanel<GroupPanel>(0);
AddAssert("keyboard selected panel is group", GetKeyboardSelectedPanel, Is.TypeOf<GroupPanel>);
ClickVisiblePanel<PanelGroup>(0);
AddAssert("keyboard selected panel is group", GetKeyboardSelectedPanel, Is.TypeOf<PanelGroup>);
AddAssert("keyboard selected panel is expanded", () => GetKeyboardSelectedPanel()?.Expanded.Value, () => Is.True);

AddAssert("selected panel is still beatmap", GetSelectedPanel, Is.TypeOf<BeatmapPanel>);
AddAssert("selected panel is still beatmap", GetSelectedPanel, Is.TypeOf<PanelBeatmap>);
}

[Test]
Expand All @@ -146,7 +146,7 @@ public void TestKeyboardSelection()
// open first group
Select();
CheckNoSelection();
AddUntilStep("some beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));
AddUntilStep("some beatmaps visible", () => Carousel.ChildrenOfType<PanelBeatmap>().Count(p => p.Alpha > 0), () => Is.GreaterThan(0));

SelectNextPanel();
Select();
Expand All @@ -171,23 +171,23 @@ public void TestKeyboardSelection()
[Test]
public void TestInputHandlingWithinGaps()
{
AddAssert("no beatmaps visible", () => !GetVisiblePanels<BeatmapPanel>().Any());
AddAssert("no beatmaps visible", () => !GetVisiblePanels<PanelBeatmap>().Any());

// Clicks just above the first group panel should not actuate any action.
ClickVisiblePanelWithOffset<GroupPanel>(0, new Vector2(0, -(GroupPanel.HEIGHT / 2 + 1)));
ClickVisiblePanelWithOffset<PanelGroup>(0, new Vector2(0, -(PanelGroup.HEIGHT / 2 + 1)));

AddAssert("no beatmaps visible", () => !GetVisiblePanels<BeatmapPanel>().Any());
AddAssert("no beatmaps visible", () => !GetVisiblePanels<PanelBeatmap>().Any());

ClickVisiblePanelWithOffset<GroupPanel>(0, new Vector2(0, -(GroupPanel.HEIGHT / 2)));
ClickVisiblePanelWithOffset<PanelGroup>(0, new Vector2(0, -(PanelGroup.HEIGHT / 2)));

AddUntilStep("wait for beatmaps visible", () => GetVisiblePanels<BeatmapPanel>().Any());
AddUntilStep("wait for beatmaps visible", () => GetVisiblePanels<PanelBeatmap>().Any());
CheckNoSelection();

// Beatmap panels expand their selection area to cover holes from spacing.
ClickVisiblePanelWithOffset<BeatmapPanel>(0, new Vector2(0, -(CarouselItem.DEFAULT_HEIGHT / 2 + 1)));
ClickVisiblePanelWithOffset<PanelBeatmap>(0, new Vector2(0, -(CarouselItem.DEFAULT_HEIGHT / 2 + 1)));
WaitForGroupSelection(0, 0);

ClickVisiblePanelWithOffset<BeatmapPanel>(1, new Vector2(0, (CarouselItem.DEFAULT_HEIGHT / 2 + 1)));
ClickVisiblePanelWithOffset<PanelBeatmap>(1, new Vector2(0, (CarouselItem.DEFAULT_HEIGHT / 2 + 1)));
WaitForGroupSelection(0, 1);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,27 +213,27 @@ public void TestInputHandlingWithinGaps()
AddBeatmaps(2, 5);
WaitForDrawablePanels();

AddAssert("no beatmaps visible", () => !GetVisiblePanels<BeatmapPanel>().Any());
AddAssert("no beatmaps visible", () => !GetVisiblePanels<PanelBeatmap>().Any());

// Clicks just above the first group panel should not actuate any action.
ClickVisiblePanelWithOffset<BeatmapSetPanel>(0, new Vector2(0, -(BeatmapSetPanel.HEIGHT / 2 + 1)));
ClickVisiblePanelWithOffset<PanelBeatmapSet>(0, new Vector2(0, -(PanelBeatmapSet.HEIGHT / 2 + 1)));

AddAssert("no beatmaps visible", () => !GetVisiblePanels<BeatmapPanel>().Any());
AddAssert("no beatmaps visible", () => !GetVisiblePanels<PanelBeatmap>().Any());

ClickVisiblePanelWithOffset<BeatmapSetPanel>(0, new Vector2(0, -(BeatmapSetPanel.HEIGHT / 2)));
ClickVisiblePanelWithOffset<PanelBeatmapSet>(0, new Vector2(0, -(PanelBeatmapSet.HEIGHT / 2)));

AddUntilStep("wait for beatmaps visible", () => GetVisiblePanels<BeatmapPanel>().Any());
AddUntilStep("wait for beatmaps visible", () => GetVisiblePanels<PanelBeatmap>().Any());
WaitForSelection(0, 0);

// Beatmap panels expand their selection area to cover holes from spacing.
ClickVisiblePanelWithOffset<BeatmapPanel>(1, new Vector2(0, -(CarouselItem.DEFAULT_HEIGHT / 2 + 1)));
ClickVisiblePanelWithOffset<PanelBeatmap>(1, new Vector2(0, -(CarouselItem.DEFAULT_HEIGHT / 2 + 1)));
WaitForSelection(0, 0);

// Panels with higher depth will handle clicks in the gutters for simplicity.
ClickVisiblePanelWithOffset<BeatmapPanel>(2, new Vector2(0, (CarouselItem.DEFAULT_HEIGHT / 2 + 1)));
ClickVisiblePanelWithOffset<PanelBeatmap>(2, new Vector2(0, (CarouselItem.DEFAULT_HEIGHT / 2 + 1)));
WaitForSelection(0, 2);

ClickVisiblePanelWithOffset<BeatmapPanel>(3, new Vector2(0, (CarouselItem.DEFAULT_HEIGHT / 2 + 1)));
ClickVisiblePanelWithOffset<PanelBeatmap>(3, new Vector2(0, (CarouselItem.DEFAULT_HEIGHT / 2 + 1)));
WaitForSelection(0, 3);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ public void TestScrollPositionMaintainedOnAddSecondSelected()
Quad positionBefore = default;

AddStep("select middle beatmap", () => Carousel.CurrentSelection = BeatmapSets.ElementAt(BeatmapSets.Count - 2).Beatmaps.First());
AddStep("scroll to selected item", () => Scroll.ScrollTo(Scroll.ChildrenOfType<BeatmapPanel>().Single(p => p.Selected.Value)));
AddStep("scroll to selected item", () => Scroll.ScrollTo(Scroll.ChildrenOfType<PanelBeatmap>().Single(p => p.Selected.Value)));

WaitForScrolling();

AddStep("save selected screen position", () => positionBefore = Carousel.ChildrenOfType<BeatmapPanel>().FirstOrDefault(p => p.Selected.Value)!.ScreenSpaceDrawQuad);
AddStep("save selected screen position", () => positionBefore = Carousel.ChildrenOfType<PanelBeatmap>().FirstOrDefault(p => p.Selected.Value)!.ScreenSpaceDrawQuad);

RemoveFirstBeatmap();
WaitForSorting();

AddAssert("select screen position unchanged", () => Carousel.ChildrenOfType<BeatmapPanel>().Single(p => p.Selected.Value).ScreenSpaceDrawQuad,
AddAssert("select screen position unchanged", () => Carousel.ChildrenOfType<PanelBeatmap>().Single(p => p.Selected.Value).ScreenSpaceDrawQuad,
() => Is.EqualTo(positionBefore));
}

Expand All @@ -54,11 +54,11 @@ public void TestScrollPositionMaintainedOnAddLastSelected()

WaitForScrolling();

AddStep("save selected screen position", () => positionBefore = Carousel.ChildrenOfType<BeatmapPanel>().FirstOrDefault(p => p.Selected.Value)!.ScreenSpaceDrawQuad);
AddStep("save selected screen position", () => positionBefore = Carousel.ChildrenOfType<PanelBeatmap>().FirstOrDefault(p => p.Selected.Value)!.ScreenSpaceDrawQuad);

RemoveFirstBeatmap();
WaitForSorting();
AddAssert("select screen position unchanged", () => Carousel.ChildrenOfType<BeatmapPanel>().Single(p => p.Selected.Value).ScreenSpaceDrawQuad,
AddAssert("select screen position unchanged", () => Carousel.ChildrenOfType<PanelBeatmap>().Single(p => p.Selected.Value).ScreenSpaceDrawQuad,
() => Is.EqualTo(positionBefore));
}
}
Expand Down
Loading
Loading