1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-12 23:12:59 +08:00

Merge pull request #31787 from peppy/carousel-v2-expanded-state

Add expanded state tracking to beatmap carousel v2
This commit is contained in:
Bartłomiej Dach 2025-02-04 13:07:21 +01:00 committed by GitHub
commit c5c1861d4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 92 additions and 30 deletions

View File

@ -153,7 +153,8 @@ namespace osu.Game.Tests.Visual.SongSelect
var groupingFilter = Carousel.Filters.OfType<BeatmapCarouselFilterGrouping>().Single(); var groupingFilter = Carousel.Filters.OfType<BeatmapCarouselFilterGrouping>().Single();
GroupDefinition g = groupingFilter.GroupItems.Keys.ElementAt(group); GroupDefinition g = groupingFilter.GroupItems.Keys.ElementAt(group);
CarouselItem item = groupingFilter.GroupItems[g].ElementAt(panel); // offset by one because the group itself is included in the items list.
CarouselItem item = groupingFilter.GroupItems[g].ElementAt(panel + 1);
return ReferenceEquals(Carousel.CurrentSelection, item.Model); return ReferenceEquals(Carousel.CurrentSelection, item.Model);
}); });

View File

@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.SongSelect
} }
[Test] [Test]
public void TestOpenCloseGroupWithNoSelection() public void TestOpenCloseGroupWithNoSelectionMouse()
{ {
AddBeatmaps(10, 5); AddBeatmaps(10, 5);
WaitForDrawablePanels(); WaitForDrawablePanels();
@ -41,6 +41,29 @@ namespace osu.Game.Tests.Visual.SongSelect
CheckNoSelection(); CheckNoSelection();
} }
[Test]
public void TestOpenCloseGroupWithNoSelectionKeyboard()
{
AddBeatmaps(10, 5);
WaitForDrawablePanels();
AddAssert("no beatmaps visible", () => Carousel.ChildrenOfType<BeatmapPanel>().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));
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);
AddAssert("keyboard selected is collapsed", () => getKeyboardSelectedPanel()?.Expanded.Value, () => Is.False);
CheckNoSelection();
GroupPanel? getKeyboardSelectedPanel() => Carousel.ChildrenOfType<GroupPanel>().SingleOrDefault(p => p.KeyboardSelected.Value);
}
[Test] [Test]
public void TestCarouselRemembersSelection() public void TestCarouselRemembersSelection()
{ {

View File

@ -105,12 +105,12 @@ namespace osu.Game.Screens.SelectV2
// Special case collapsing an open group. // Special case collapsing an open group.
if (lastSelectedGroup == group) if (lastSelectedGroup == group)
{ {
setVisibilityOfGroupItems(lastSelectedGroup, false); setExpansionStateOfGroup(lastSelectedGroup, false);
lastSelectedGroup = null; lastSelectedGroup = null;
return false; return false;
} }
setVisibleGroup(group); setExpandedGroup(group);
return false; return false;
case BeatmapSetInfo setInfo: case BeatmapSetInfo setInfo:
@ -127,11 +127,11 @@ namespace osu.Game.Screens.SelectV2
GroupDefinition? group = grouping.GroupItems.SingleOrDefault(kvp => kvp.Value.Any(i => ReferenceEquals(i.Model, beatmapInfo))).Key; GroupDefinition? group = grouping.GroupItems.SingleOrDefault(kvp => kvp.Value.Any(i => ReferenceEquals(i.Model, beatmapInfo))).Key;
if (group != null) if (group != null)
setVisibleGroup(group); setExpandedGroup(group);
} }
else else
{ {
setVisibleSet(beatmapInfo); setExpandedSet(beatmapInfo);
} }
return true; return true;
@ -158,37 +158,47 @@ namespace osu.Game.Screens.SelectV2
} }
} }
private void setVisibleGroup(GroupDefinition group) private void setExpandedGroup(GroupDefinition group)
{ {
if (lastSelectedGroup != null) if (lastSelectedGroup != null)
setVisibilityOfGroupItems(lastSelectedGroup, false); setExpansionStateOfGroup(lastSelectedGroup, false);
lastSelectedGroup = group; lastSelectedGroup = group;
setVisibilityOfGroupItems(group, true); setExpansionStateOfGroup(group, true);
} }
private void setVisibilityOfGroupItems(GroupDefinition group, bool visible) private void setExpansionStateOfGroup(GroupDefinition group, bool expanded)
{ {
if (grouping.GroupItems.TryGetValue(group, out var items)) if (grouping.GroupItems.TryGetValue(group, out var items))
{ {
foreach (var i in items) foreach (var i in items)
i.IsVisible = visible; {
if (i.Model is GroupDefinition)
i.IsExpanded = expanded;
else
i.IsVisible = expanded;
}
} }
} }
private void setVisibleSet(BeatmapInfo beatmapInfo) private void setExpandedSet(BeatmapInfo beatmapInfo)
{ {
if (lastSelectedBeatmap != null) if (lastSelectedBeatmap != null)
setVisibilityOfSetItems(lastSelectedBeatmap.BeatmapSet!, false); setExpansionStateOfSetItems(lastSelectedBeatmap.BeatmapSet!, false);
lastSelectedBeatmap = beatmapInfo; lastSelectedBeatmap = beatmapInfo;
setVisibilityOfSetItems(beatmapInfo.BeatmapSet!, true); setExpansionStateOfSetItems(beatmapInfo.BeatmapSet!, true);
} }
private void setVisibilityOfSetItems(BeatmapSetInfo set, bool visible) private void setExpansionStateOfSetItems(BeatmapSetInfo set, bool expanded)
{ {
if (grouping.SetItems.TryGetValue(set, out var items)) if (grouping.SetItems.TryGetValue(set, out var items))
{ {
foreach (var i in items) foreach (var i in items)
i.IsVisible = visible; {
if (i.Model is BeatmapSetInfo)
i.IsExpanded = expanded;
else
i.IsVisible = expanded;
}
} }
} }

View File

@ -65,7 +65,11 @@ namespace osu.Game.Screens.SelectV2
if (b.StarRating > starGroup) if (b.StarRating > starGroup)
{ {
starGroup = (int)Math.Floor(b.StarRating); starGroup = (int)Math.Floor(b.StarRating);
newItems.Add(new CarouselItem(new GroupDefinition($"{starGroup} - {++starGroup} *")) { DrawHeight = GroupPanel.HEIGHT }); var groupDefinition = new GroupDefinition($"{starGroup} - {++starGroup} *");
var groupItem = new CarouselItem(groupDefinition) { DrawHeight = GroupPanel.HEIGHT };
newItems.Add(groupItem);
groupItems[groupDefinition] = new HashSet<CarouselItem> { groupItem };
} }
newItems.Add(item); newItems.Add(item);
@ -91,14 +95,13 @@ namespace osu.Game.Screens.SelectV2
if (newBeatmapSet) if (newBeatmapSet)
{ {
newItems.Insert(i, new CarouselItem(beatmap.BeatmapSet!) { DrawHeight = BeatmapSetPanel.HEIGHT }); var setItem = new CarouselItem(beatmap.BeatmapSet!) { DrawHeight = BeatmapSetPanel.HEIGHT };
setItems[beatmap.BeatmapSet!] = new HashSet<CarouselItem> { setItem };
newItems.Insert(i, setItem);
i++; i++;
} }
if (!setItems.TryGetValue(beatmap.BeatmapSet!, out var related)) setItems[beatmap.BeatmapSet!].Add(item);
setItems[beatmap.BeatmapSet!] = related = new HashSet<CarouselItem>();
related.Add(item);
item.IsVisible = false; item.IsVisible = false;
} }
@ -121,10 +124,7 @@ namespace osu.Game.Screens.SelectV2
if (lastGroup != null) if (lastGroup != null)
{ {
if (!groupItems.TryGetValue(lastGroup, out var groupRelated)) groupItems[lastGroup].Add(item);
groupItems[lastGroup] = groupRelated = new HashSet<CarouselItem>();
groupRelated.Add(item);
item.IsVisible = false; item.IsVisible = false;
} }
} }

View File

@ -100,6 +100,7 @@ namespace osu.Game.Screens.SelectV2
public CarouselItem? Item { get; set; } public CarouselItem? Item { get; set; }
public BindableBool Selected { get; } = new BindableBool(); public BindableBool Selected { get; } = new BindableBool();
public BindableBool Expanded { get; } = new BindableBool();
public BindableBool KeyboardSelected { get; } = new BindableBool(); public BindableBool KeyboardSelected { get; } = new BindableBool();
public double DrawYPosition { get; set; } public double DrawYPosition { get; set; }

View File

@ -25,6 +25,7 @@ namespace osu.Game.Screens.SelectV2
private BeatmapCarousel carousel { get; set; } = null!; private BeatmapCarousel carousel { get; set; } = null!;
private OsuSpriteText text = null!; private OsuSpriteText text = null!;
private Box box = null!;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
@ -34,7 +35,7 @@ namespace osu.Game.Screens.SelectV2
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new Box box = new Box
{ {
Colour = Color4.Yellow.Darken(5), Colour = Color4.Yellow.Darken(5),
Alpha = 0.8f, Alpha = 0.8f,
@ -48,6 +49,11 @@ namespace osu.Game.Screens.SelectV2
} }
}; };
Expanded.BindValueChanged(value =>
{
box.FadeColour(value.NewValue ? Color4.Yellow.Darken(2) : Color4.Yellow.Darken(5), 500, Easing.OutQuint);
});
KeyboardSelected.BindValueChanged(value => KeyboardSelected.BindValueChanged(value =>
{ {
if (value.NewValue) if (value.NewValue)
@ -85,6 +91,7 @@ namespace osu.Game.Screens.SelectV2
public CarouselItem? Item { get; set; } public CarouselItem? Item { get; set; }
public BindableBool Selected { get; } = new BindableBool(); public BindableBool Selected { get; } = new BindableBool();
public BindableBool Expanded { get; } = new BindableBool();
public BindableBool KeyboardSelected { get; } = new BindableBool(); public BindableBool KeyboardSelected { get; } = new BindableBool();
public double DrawYPosition { get; set; } public double DrawYPosition { get; set; }

View File

@ -571,6 +571,7 @@ namespace osu.Game.Screens.SelectV2
c.Selected.Value = c.Item == currentSelection?.CarouselItem; c.Selected.Value = c.Item == currentSelection?.CarouselItem;
c.KeyboardSelected.Value = c.Item == currentKeyboardSelection?.CarouselItem; c.KeyboardSelected.Value = c.Item == currentKeyboardSelection?.CarouselItem;
c.Expanded.Value = c.Item.IsExpanded;
} }
} }
@ -674,6 +675,7 @@ namespace osu.Game.Screens.SelectV2
carouselPanel.Item = null; carouselPanel.Item = null;
carouselPanel.Selected.Value = false; carouselPanel.Selected.Value = false;
carouselPanel.KeyboardSelected.Value = false; carouselPanel.KeyboardSelected.Value = false;
carouselPanel.Expanded.Value = false;
} }
#endregion #endregion

View File

@ -30,10 +30,15 @@ namespace osu.Game.Screens.SelectV2
public float DrawHeight { get; set; } = DEFAULT_HEIGHT; public float DrawHeight { get; set; } = DEFAULT_HEIGHT;
/// <summary> /// <summary>
/// Whether this item is visible or collapsed (hidden). /// Whether this item is visible or hidden.
/// </summary> /// </summary>
public bool IsVisible { get; set; } = true; public bool IsVisible { get; set; } = true;
/// <summary>
/// Whether this item is expanded or not. Should only be used for headers of groups.
/// </summary>
public bool IsExpanded { get; set; }
public CarouselItem(object model) public CarouselItem(object model)
{ {
Model = model; Model = model;

View File

@ -26,6 +26,8 @@ namespace osu.Game.Screens.SelectV2
private Box activationFlash = null!; private Box activationFlash = null!;
private OsuSpriteText text = null!; private OsuSpriteText text = null!;
private Box box = null!;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
@ -34,7 +36,7 @@ namespace osu.Game.Screens.SelectV2
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new Box box = new Box
{ {
Colour = Color4.DarkBlue.Darken(5), Colour = Color4.DarkBlue.Darken(5),
Alpha = 0.8f, Alpha = 0.8f,
@ -60,6 +62,11 @@ namespace osu.Game.Screens.SelectV2
activationFlash.FadeTo(value.NewValue ? 0.2f : 0, 500, Easing.OutQuint); activationFlash.FadeTo(value.NewValue ? 0.2f : 0, 500, Easing.OutQuint);
}); });
Expanded.BindValueChanged(value =>
{
box.FadeColour(value.NewValue ? Color4.SkyBlue : Color4.DarkBlue.Darken(5), 500, Easing.OutQuint);
});
KeyboardSelected.BindValueChanged(value => KeyboardSelected.BindValueChanged(value =>
{ {
if (value.NewValue) if (value.NewValue)
@ -97,6 +104,7 @@ namespace osu.Game.Screens.SelectV2
public CarouselItem? Item { get; set; } public CarouselItem? Item { get; set; }
public BindableBool Selected { get; } = new BindableBool(); public BindableBool Selected { get; } = new BindableBool();
public BindableBool Expanded { get; } = new BindableBool();
public BindableBool KeyboardSelected { get; } = new BindableBool(); public BindableBool KeyboardSelected { get; } = new BindableBool();
public double DrawYPosition { get; set; } public double DrawYPosition { get; set; }

View File

@ -14,10 +14,15 @@ namespace osu.Game.Screens.SelectV2
public interface ICarouselPanel public interface ICarouselPanel
{ {
/// <summary> /// <summary>
/// Whether this item has selection. Should be read from to update the visual state. /// Whether this item has selection (see <see cref="Carousel{T}.CurrentSelection"/>). Should be read from to update the visual state.
/// </summary> /// </summary>
BindableBool Selected { get; } BindableBool Selected { get; }
/// <summary>
/// Whether this item is expanded (see <see cref="CarouselItem.IsExpanded"/>). Should be read from to update the visual state.
/// </summary>
BindableBool Expanded { get; }
/// <summary> /// <summary>
/// Whether this item has keyboard selection. Should be read from to update the visual state. /// Whether this item has keyboard selection. Should be read from to update the visual state.
/// </summary> /// </summary>