1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-28 06:42:54 +08:00

Add recent participants

This commit is contained in:
smoogipoo 2021-07-13 15:10:44 +09:00
parent c1fba3da6b
commit 8c4a257742
4 changed files with 381 additions and 8 deletions

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
@ -31,28 +32,24 @@ namespace osu.Game.Tests.Visual.Multiplayer
Name = { Value = "Room 1" },
Status = { Value = new RoomStatusOpen() },
EndDate = { Value = DateTimeOffset.Now.AddDays(1) },
Host = { Value = new User { Username = "peppy", Id = 2 } }
}),
createDrawableRoom(new Room
{
Name = { Value = "Room 2" },
Status = { Value = new RoomStatusPlaying() },
EndDate = { Value = DateTimeOffset.Now.AddDays(1) },
Host = { Value = new User { Username = "peppy", Id = 2 } }
}),
createDrawableRoom(new Room
{
Name = { Value = "Room 3" },
Status = { Value = new RoomStatusEnded() },
EndDate = { Value = DateTimeOffset.Now },
Host = { Value = new User { Username = "peppy", Id = 2 } }
}),
createDrawableRoom(new Room
{
Name = { Value = "Room 4" },
Status = { Value = new RoomStatusOpen() },
Category = { Value = RoomCategory.Realtime },
Host = { Value = new User { Username = "peppy", Id = 2 } }
}),
}
};
@ -60,11 +57,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
private DrawableRoom createDrawableRoom(Room room)
{
var drawableRoom = new DrawableRoom(room)
{
MatchingFilter = true
};
room.Host.Value ??= new User { Username = "peppy", Id = 2 };
if (room.RecentParticipants.Count == 0)
{
room.RecentParticipants.AddRange(Enumerable.Range(0, 20).Select(i => new User
{
Id = i,
Username = $"User {i}"
}));
}
var drawableRoom = new DrawableRoom(room) { MatchingFilter = true };
drawableRoom.Action = () => drawableRoom.State = drawableRoom.State == SelectionState.Selected ? SelectionState.NotSelected : SelectionState.Selected;
return drawableRoom;

View File

@ -0,0 +1,93 @@
// 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.Graphics;
using osu.Framework.Testing;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Tests.Visual.OnlinePlay;
using osu.Game.Users;
using osu.Game.Users.Drawables;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneRecentParticipantsList : OnlinePlayTestScene
{
private RecentParticipantsList list;
[SetUp]
public new void Setup() => Schedule(() =>
{
SelectedRoom.Value = new Room { Name = { Value = "test room" } };
Child = list = new RecentParticipantsList
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
NumberOfAvatars = 3
};
});
[Test]
public void TestAvatarCount()
{
AddStep("add 50 users", () =>
{
for (int i = 0; i < 50; i++)
{
SelectedRoom.Value.RecentParticipants.Add(new User
{
Id = i,
Username = $"User {i}"
});
}
});
AddStep("set 3 avatars", () => list.NumberOfAvatars = 3);
AddAssert("3 avatars displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 3);
AddAssert("47 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 47);
AddStep("set 10 avatars", () => list.NumberOfAvatars = 10);
AddAssert("10 avatars displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 10);
AddAssert("40 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 40);
}
[Test]
public void TestAddAndRemoveUsers()
{
AddStep("add 50 users", () =>
{
for (int i = 0; i < 50; i++)
{
SelectedRoom.Value.RecentParticipants.Add(new User
{
Id = i,
Username = $"User {i}"
});
}
});
AddStep("remove from start", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0));
AddAssert("3 avatars displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 3);
AddAssert("46 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 46);
AddStep("remove from end", () => SelectedRoom.Value.RecentParticipants.RemoveAt(SelectedRoom.Value.RecentParticipants.Count - 1));
AddAssert("3 avatars displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 3);
AddAssert("45 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 45);
AddRepeatStep("remove 45 users", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0), 45);
AddAssert("3 avatars displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 3);
AddAssert("0 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 0);
AddAssert("hidden users bubble hidden", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Alpha < 0.5f);
AddStep("remove another user", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0));
AddAssert("2 avatars displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 2);
AddAssert("0 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 0);
AddRepeatStep("remove the remaining two users", () => SelectedRoom.Value.RecentParticipants.RemoveAt(0), 2);
AddAssert("0 avatars displayed", () => !list.ChildrenOfType<UpdateableAvatar>().Any());
}
}
}

View File

@ -0,0 +1,238 @@
// 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.Specialized;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using osu.Game.Users.Drawables;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay.Components
{
public class RecentParticipantsList : OnlinePlayComposite
{
private const float avatar_size = 36;
private FillFlowContainer<CircularAvatar> avatarFlow;
private HiddenUserCount hiddenUsers;
public RecentParticipantsList()
{
AutoSizeAxes = Axes.X;
Height = 60;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
MaskingSmoothness = 2,
CornerRadius = 10,
Shear = new Vector2(0.2f, 0),
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex(@"#2E3835")
}
},
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(4),
Padding = new MarginPadding { Left = 8, Right = 16 },
Children = new Drawable[]
{
new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(16),
Icon = FontAwesome.Solid.User,
},
avatarFlow = new FillFlowContainer<CircularAvatar>
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(4)
},
hiddenUsers = new HiddenUserCount
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
}
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
RecentParticipants.BindCollectionChanged(onParticipantsChanged, true);
}
private int numberOfAvatars = 3;
public int NumberOfAvatars
{
get => numberOfAvatars;
set
{
numberOfAvatars = value;
if (LoadState < LoadState.Loaded)
return;
// Reinitialising the list looks janky, but this is unlikely to be used in a setting where it's visible.
clearUsers();
foreach (var u in RecentParticipants)
addUser(u);
}
}
private void onParticipantsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var added in e.NewItems.OfType<User>())
addUser(added);
break;
case NotifyCollectionChangedAction.Remove:
foreach (var removed in e.OldItems.OfType<User>())
removeUser(removed);
break;
case NotifyCollectionChangedAction.Reset:
clearUsers();
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
// Easiest is to just reinitialise the whole list. These are unlikely to ever be use cases.
clearUsers();
foreach (var u in RecentParticipants)
addUser(u);
break;
}
}
private void addUser(User user)
{
if (avatarFlow.Count < NumberOfAvatars)
avatarFlow.Add(new CircularAvatar { User = user });
else
hiddenUsers.Count++;
}
private void removeUser(User user)
{
if (avatarFlow.RemoveAll(a => a.User == user) > 0)
{
if (RecentParticipants.Count >= NumberOfAvatars)
{
avatarFlow.Add(new CircularAvatar { User = RecentParticipants.First(u => avatarFlow.All(a => a.User != u)) });
hiddenUsers.Count--;
}
}
else
hiddenUsers.Count--;
}
private void clearUsers()
{
avatarFlow.Clear();
hiddenUsers.Count = 0;
}
private class CircularAvatar : CompositeDrawable
{
public User User
{
get => avatar.User;
set => avatar.User = value;
}
private readonly UpdateableAvatar avatar;
public CircularAvatar()
{
Size = new Vector2(avatar_size);
InternalChild = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Child = avatar = new UpdateableAvatar(showUsernameTooltip: true) { RelativeSizeAxes = Axes.Both }
};
}
}
public class HiddenUserCount : CompositeDrawable
{
public int Count
{
get => count;
set
{
count = value;
countText.Text = $"+{count}";
if (count > 0)
Show();
else
Hide();
}
}
private int count;
private readonly SpriteText countText;
public HiddenUserCount()
{
Size = new Vector2(avatar_size);
Alpha = 0;
InternalChild = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
countText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
}
};
}
}
}
}

View File

@ -91,6 +91,22 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
}
}
private int numberOfAvatars = 3;
public int NumberOfAvatars
{
get => numberOfAvatars;
set
{
numberOfAvatars = value;
if (recentParticipantsList != null)
recentParticipantsList.NumberOfAvatars = value;
}
}
private RecentParticipantsList recentParticipantsList;
public bool FilteringActive { get; set; }
public DrawableRoom(Room room)
@ -228,6 +244,28 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
}
}
},
new FillFlowContainer
{
Name = "Right content",
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Padding = new MarginPadding
{
Right = 10,
Vertical = 5
},
Children = new Drawable[]
{
recentParticipantsList = new RecentParticipantsList
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
NumberOfAvatars = NumberOfAvatars
}
}
}
},
},
},