mirror of
https://github.com/ppy/osu.git
synced 2026-05-26 13:50:33 +08:00
Merge pull request #32757 from smoogipoo/playlist-completion-icon
Add display to show completed playlist items
This commit is contained in:
@@ -105,6 +105,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("no item selected", () => playlist.SelectedItem.Value == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMarkCompleted()
|
||||
{
|
||||
createPlaylist();
|
||||
AddStep("mark some items as complete", () =>
|
||||
{
|
||||
playlist.Items[0].MarkCompleted();
|
||||
playlist.Items[2].MarkCompleted();
|
||||
playlist.Items[3].MarkCompleted();
|
||||
playlist.Items[5].MarkCompleted();
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectable()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation
|
||||
{
|
||||
public static class DrawableRoomPlaylistItemStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.DrawableRoomPlaylistItem";
|
||||
|
||||
/// <summary>
|
||||
/// "You have completed this beatmap"
|
||||
/// </summary>
|
||||
public static LocalisableString CompletedTooltip => new TranslatableString(getKey(@"completed_tooltip"), @"You have completed this beatmap");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,22 @@ namespace osu.Game.Online.Rooms
|
||||
/// </summary>
|
||||
public class ItemAttemptsCount
|
||||
{
|
||||
/// <summary>
|
||||
/// The playlist item this object describes.
|
||||
/// </summary>
|
||||
[JsonProperty("id")]
|
||||
public int PlaylistItemID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of times the user attempted the playlist item.
|
||||
/// </summary>
|
||||
[JsonProperty("attempts")]
|
||||
public int Attempts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the user has a passing score on the playlist item.
|
||||
/// </summary>
|
||||
[JsonProperty("passed")]
|
||||
public bool Passed { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,11 @@ namespace osu.Game.Online.Rooms
|
||||
|
||||
private readonly Bindable<bool> valid = new BindableBool(true);
|
||||
|
||||
[JsonIgnore]
|
||||
public IBindable<bool> Completed => completed;
|
||||
|
||||
private readonly Bindable<bool> completed = new BindableBool(false);
|
||||
|
||||
[JsonConstructor]
|
||||
private PlaylistItem()
|
||||
: this(new APIBeatmap())
|
||||
@@ -118,6 +123,8 @@ namespace osu.Game.Online.Rooms
|
||||
|
||||
public void MarkInvalid() => valid.Value = false;
|
||||
|
||||
public void MarkCompleted() => completed.Value = true;
|
||||
|
||||
#region Newtonsoft.Json implicit ShouldSerialize() methods
|
||||
|
||||
// The properties in this region are used implicitly by Newtonsoft.Json to not serialise certain fields in some cases.
|
||||
|
||||
@@ -31,13 +31,13 @@ using osu.Game.Online.Chat;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Localisation;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
@@ -76,6 +76,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
|
||||
private readonly DelayedLoadWrapper onScreenLoader;
|
||||
private readonly IBindable<bool> valid = new Bindable<bool>();
|
||||
private readonly IBindable<bool> completed = new Bindable<bool>();
|
||||
|
||||
private IBeatmapInfo? beatmap;
|
||||
private IRulesetInfo? ruleset;
|
||||
@@ -128,6 +129,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
Item = item;
|
||||
|
||||
valid.BindTo(item.Valid);
|
||||
completed.BindTo(item.Completed);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -525,9 +527,27 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
|
||||
private IEnumerable<Drawable> createButtons() => new[]
|
||||
{
|
||||
beatmap == null ? Empty() : new PlaylistDownloadButton(beatmap),
|
||||
new CompletionIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Visible = { BindTarget = completed }
|
||||
},
|
||||
beatmap == null
|
||||
? Empty().With(d =>
|
||||
{
|
||||
d.Anchor = Anchor.Centre;
|
||||
d.Origin = Anchor.Centre;
|
||||
})
|
||||
: new PlaylistDownloadButton(beatmap)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
showResultsButton = new GrayButton(FontAwesome.Solid.ChartPie)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(30, 30),
|
||||
Action = () => RequestResults?.Invoke(Item),
|
||||
Alpha = AllowShowingResults ? 1 : 0,
|
||||
@@ -535,13 +555,17 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
},
|
||||
editButton = new PlaylistEditButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(30, 30),
|
||||
Alpha = AllowEditing ? 1 : 0,
|
||||
Action = () => RequestEdit?.Invoke(Item),
|
||||
TooltipText = CommonStrings.ButtonsEdit
|
||||
TooltipText = Resources.Localisation.Web.CommonStrings.ButtonsEdit
|
||||
},
|
||||
removeButton = new PlaylistRemoveButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(30, 30),
|
||||
Alpha = AllowDeletion ? 1 : 0,
|
||||
Action = () => RequestDeletion?.Invoke(Item),
|
||||
@@ -768,5 +792,64 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
this.allowInteraction = allowInteraction;
|
||||
}
|
||||
}
|
||||
|
||||
private partial class CompletionIcon : CompositeDrawable, IHasTooltip
|
||||
{
|
||||
public readonly BindableBool Visible = new BindableBool();
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(16),
|
||||
Masking = true,
|
||||
Colour = colours.Lime0,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(0.5f),
|
||||
Colour = OsuColour.Gray(0.5f),
|
||||
Icon = FontAwesome.Solid.Check
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Visible.BindValueChanged(onVisibleChanged, true);
|
||||
}
|
||||
|
||||
private void onVisibleChanged(ValueChangedEvent<bool> visible)
|
||||
{
|
||||
if (visible.NewValue)
|
||||
{
|
||||
Size = new Vector2(16);
|
||||
Alpha = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Size = Vector2.Zero;
|
||||
Alpha = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public LocalisableString TooltipText => DrawableRoomPlaylistItemStrings.CompletedTooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,6 +465,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
});
|
||||
|
||||
updateSetupState();
|
||||
updateUserScore();
|
||||
updateGameplayState();
|
||||
}
|
||||
|
||||
@@ -480,6 +481,10 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
case nameof(Room.RoomID):
|
||||
updateSetupState();
|
||||
break;
|
||||
|
||||
case nameof(Room.UserScore):
|
||||
updateUserScore();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,12 +512,32 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
progressSection.Alpha = room.MaxAttempts != null ? 1 : 0;
|
||||
drawablePlaylist.Items.ReplaceRange(0, drawablePlaylist.Items.Count, room.Playlist);
|
||||
|
||||
updateUserScore();
|
||||
|
||||
// Select an initial item for the user to help them get into a playable state quicker.
|
||||
SelectedItem.Value = room.Playlist.FirstOrDefault();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Responds to changes in <see cref="Room.UserScore"/> to mark playlist items as completed.
|
||||
/// </summary>
|
||||
private void updateUserScore()
|
||||
{
|
||||
if (room.UserScore == null)
|
||||
return;
|
||||
|
||||
if (drawablePlaylist.Items.Count == 0)
|
||||
return;
|
||||
|
||||
foreach (var item in room.UserScore.PlaylistItemAttempts)
|
||||
{
|
||||
if (item.Passed)
|
||||
drawablePlaylist.Items.Single(i => i.ID == item.PlaylistItemID).MarkCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the rate at which the <see cref="Room"/> is updated.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user