Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
77 changes: 64 additions & 13 deletions osu.Game.Tests/Visual/Ranking/TestSceneUserTagControl.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
Expand Down Expand Up @@ -30,6 +31,26 @@ public partial class TestSceneUserTagControl : OsuManualInputManagerTestScene

private int writeRequestCount;

private Container content = null!;

protected override Container<Drawable> Content => content;

[BackgroundDependencyLoader]
private void load()
{
base.Content.Child = new PopoverContainer
{
RelativeSizeAxes = Axes.Both,
Child = content = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 700,
AutoSizeAxes = Axes.Y,
},
};
}

[SetUpSteps]
public void SetUpSteps()
{
Expand Down Expand Up @@ -61,6 +82,7 @@ public void SetUpSteps()
},
new APITag { Id = 4, Name = "tap/bursts", Description = "Patterns requiring continuous movement and alternating, typically 9 notes or less.", },
new APITag { Id = 5, Name = "style/mono-heavy", Description = "Features monos used in large amounts.", RulesetId = 1, },
new APITag { Id = 6, Name = "style/freeform", Description = "An unrestrained and loose approach towards visual structure." },
]
}), 500);
return true;
Expand Down Expand Up @@ -138,6 +160,40 @@ public void TestNotWritable()
AddAssert("no vote requests send", () => writeRequestCount, () => Is.Zero);
}

[Test]
public void TestFilter()
{
AddStep("show", () =>
{
var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
working.BeatmapInfo.OnlineID = 42;
Beatmap.Value = working;
Child = new UserTagControl(Beatmap.Value.BeatmapInfo)
{
Filter = t => (t.GroupName ?? "").StartsWith("style", StringComparison.Ordinal),
Writable = true,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
};
});
AddUntilStep("two tags are shown", () => getTagFlow().Count, () => Is.EqualTo(2));
AddAssert("add new button is not present", () => getTagFlow().ChildrenOfType<UserTagControl.DrawableUserTag>().Where(t => t.UserTag.DisplayName == "add"), () => Is.Empty);
AddStep("vote for tag 6", () =>
{
InputManager.MoveMouseTo(getDrawableTagById(6));
InputManager.Click(MouseButton.Left);
});
AddUntilStep("tag 6 voted for", () => getDrawableTagById(6).UserTag.VoteCount.Value, () => Is.EqualTo(1));
AddStep("remove vote for tag 6", () =>
{
InputManager.MoveMouseTo(getDrawableTagById(6));
InputManager.Click(MouseButton.Left);
});
AddUntilStep("tag 6 not voted for", () => getDrawableTagById(6).UserTag.VoteCount.Value, () => Is.EqualTo(0));
AddAssert("two tags are shown", () => getTagFlow().Count, () => Is.EqualTo(2));
}

[Test]
public void TestTagsDoNotMoveUntilMouseMovesAway()
{
Expand Down Expand Up @@ -173,24 +229,19 @@ public void TestTagsDoNotMoveUntilMouseMovesAway()
AddUntilStep("tag 2 voted for", () => getDrawableTagById(2).UserTag.VoteCount.Value, () => Is.EqualTo(4));
AddStep("move mouse away", () => InputManager.MoveMouseTo(Vector2.Zero));
AddAssert("tag 2 reordered to first", () => getTagFlow().GetLayoutPosition(getDrawableTagById(2)), () => Is.EqualTo(0));

FillFlowContainer<UserTagControl.DrawableUserTag> getTagFlow() => this.ChildrenOfType<FillFlowContainer<UserTagControl.DrawableUserTag>>().Single();

UserTagControl.DrawableUserTag getDrawableTagById(long id) => getTagFlow().Single(t => t.UserTag.Id == id);
}

private FillFlowContainer<UserTagControl.DrawableUserTag> getTagFlow() => this.ChildrenOfType<FillFlowContainer<UserTagControl.DrawableUserTag>>().Single();
private UserTagControl.DrawableUserTag getDrawableTagById(long id) => getTagFlow().Single(t => t.UserTag.Id == id);

private void recreateControl(bool writable = true)
{
Child = new PopoverContainer
Child = new UserTagControl(Beatmap.Value.BeatmapInfo)
{
RelativeSizeAxes = Axes.Both,
Child = new UserTagControl(Beatmap.Value.BeatmapInfo)
{
Writable = writable,
Width = 700,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
Writable = writable,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
};
}
}
Expand Down
47 changes: 40 additions & 7 deletions osu.Game/Screens/Ranking/UserTagControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ public partial class UserTagControl : CompositeDrawable
/// </summary>
public bool Writable { private get; init; }

/// <summary>
/// Allows to override the list of displayed tags using a provided filter function.
/// </summary>
/// <remarks>
/// Tags matching the filter function will always be displayed, irrespective of the
/// amount of votes. Additionally, the 'add new tag' button will not be displayed.
/// </remarks>
public Func<UserTag, bool>? Filter { private get; init; }

private InputManager inputManager = null!;

[Resolved]
Expand Down Expand Up @@ -93,7 +102,7 @@ private void load(SessionStatics sessionStatics)
}
};

if (Writable)
if (Writable && Filter == null)
{
tagFlow.Add(addNewTagUserTag = new AddNewTagUserTag
{
Expand Down Expand Up @@ -135,17 +144,22 @@ private void updateTags()
return;

relevantTagsById.Clear();
relevantTagsById.AddRange(apiTags.Value
.Where(t => t.RulesetId == null || t.RulesetId == beatmapInfo.Ruleset.OnlineID)
.Select(t => new KeyValuePair<long, UserTag>(t.Id, new UserTag(t))));

var relevantTags = apiTags.Value
.Where(t => t.RulesetId == null || t.RulesetId == beatmapInfo.Ruleset.OnlineID)
.Select(t => new KeyValuePair<long, UserTag>(t.Id, new UserTag(t)));

if (Filter != null)
relevantTags = relevantTags.Where(kvp => Filter(kvp.Value));

relevantTagsById.AddRange(relevantTags);

foreach (var topTag in apiBeatmap.Value.TopTags ?? [])
{
if (relevantTagsById.TryGetValue(topTag.TagId, out var tag))
{
tag.VoteCount.Value = topTag.VoteCount;
tag.Updating.Value = false;
displayedTags.Add(tag);
}
}

Expand All @@ -157,6 +171,19 @@ private void updateTags()
tag.Updating.Value = false;
}
}

if (Filter == null)
{
foreach (var topTag in apiBeatmap.Value.TopTags ?? [])
{
if (relevantTagsById.TryGetValue(topTag.TagId, out var tag))
displayedTags.Add(tag);
}
}
else
{
displayedTags.AddRange(relevantTagsById.Values);
}
}

private void displayTags(object? sender, NotifyCollectionChangedEventArgs e)
Expand Down Expand Up @@ -194,7 +221,10 @@ private void displayTags(object? sender, NotifyCollectionChangedEventArgs e)
case NotifyCollectionChangedAction.Reset:
{
tagFlow.Clear();
if (Writable) tagFlow.Add(addNewTagUserTag!);

if (Writable && Filter == null)
tagFlow.Add(addNewTagUserTag!);

break;
}
}
Expand Down Expand Up @@ -230,7 +260,7 @@ private void toggleVote(UserTag tag)
{
tag.VoteCount.Value += 1;
tag.Voted.Value = true;
if (!displayedTags.Contains(tag))
if (!displayedTags.Contains(tag) && Filter == null)
displayedTags.Add(tag);
};
request = addReq;
Expand All @@ -245,6 +275,9 @@ private void toggleVote(UserTag tag)

private void voteCountChanged(ValueChangedEvent<int> _)
{
if (Filter != null)
return;

var tagsWithNoVotes = displayedTags.Where(t => t.VoteCount.Value == 0).ToArray();

foreach (var tag in tagsWithNoVotes)
Expand Down
Loading