diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorPlayerGrid.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorPlayerGrid.cs new file mode 100644 index 0000000000..c0958c7fe8 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorPlayerGrid.cs @@ -0,0 +1,115 @@ +// Copyright (c) ppy Pty Ltd . 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.Graphics.Shapes; +using osu.Framework.Utils; +using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; +using osuTK.Graphics; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerSpectatorPlayerGrid : OsuManualInputManagerTestScene + { + private PlayerGrid grid; + + [SetUp] + public void Setup() => Schedule(() => + { + Child = grid = new PlayerGrid { RelativeSizeAxes = Axes.Both }; + }); + + [Test] + public void TestMaximiseAndMinimise() + { + addCells(2); + + assertMaximisation(0, false, true); + assertMaximisation(1, false, true); + + clickCell(0); + assertMaximisation(0, true); + assertMaximisation(1, false, true); + clickCell(0); + assertMaximisation(0, false); + assertMaximisation(1, false, true); + + clickCell(1); + assertMaximisation(1, true); + assertMaximisation(0, false, true); + clickCell(1); + assertMaximisation(1, false); + assertMaximisation(0, false, true); + } + + [Test] + public void TestClickBothCellsSimultaneously() + { + addCells(2); + + AddStep("click cell 0 then 1", () => + { + InputManager.MoveMouseTo(grid.Content.ElementAt(0)); + InputManager.Click(MouseButton.Left); + + InputManager.MoveMouseTo(grid.Content.ElementAt(1)); + InputManager.Click(MouseButton.Left); + }); + + assertMaximisation(1, true); + assertMaximisation(0, false); + } + + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + [TestCase(4)] + [TestCase(5)] + [TestCase(9)] + [TestCase(11)] + [TestCase(12)] + [TestCase(15)] + [TestCase(16)] + public void TestCellCount(int count) + { + addCells(count); + AddWaitStep("wait for display", 2); + } + + private void addCells(int count) => AddStep($"add {count} grid cells", () => + { + for (int i = 0; i < count; i++) + grid.Add(new GridContent()); + }); + + private void clickCell(int index) => AddStep($"click cell index {index}", () => + { + InputManager.MoveMouseTo(grid.Content.ElementAt(index)); + InputManager.Click(MouseButton.Left); + }); + + private void assertMaximisation(int index, bool shouldBeMaximised, bool instant = false) + { + string assertionText = $"cell index {index} {(shouldBeMaximised ? "is" : "is not")} maximised"; + + if (instant) + AddAssert(assertionText, checkAction); + else + AddUntilStep(assertionText, checkAction); + + bool checkAction() => Precision.AlmostEquals(grid.MaximisedFacade.DrawSize, grid.Content.ElementAt(index).DrawSize, 10) == shouldBeMaximised; + } + + private class GridContent : Box + { + public GridContent() + { + RelativeSizeAxes = Axes.Both; + Colour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1f); + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs index a8bd1db9dc..f41948217c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs @@ -13,9 +13,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public Drawable MaximisedFacade => maximisedFacade; - private readonly PlayerGridFacade maximisedFacade; + private readonly Facade maximisedFacade; private readonly Container paddingContainer; - private readonly FillFlowContainer facadeContainer; + private readonly FillFlowContainer facadeContainer; private readonly Container cellContainer; public PlayerGrid() @@ -31,38 +31,50 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate new Container { RelativeSizeAxes = Axes.Both, - Child = facadeContainer = new FillFlowContainer + Child = facadeContainer = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Spacing = new Vector2(player_spacing), } }, - maximisedFacade = new PlayerGridFacade { RelativeSizeAxes = Axes.Both } + maximisedFacade = new Facade { RelativeSizeAxes = Axes.Both } } }, cellContainer = new Container { RelativeSizeAxes = Axes.Both } }; } - public void AddContent(Drawable content) + /// + /// Adds a new cell with content to this grid. + /// + /// The content the cell should contain. + /// If more than 16 cells are added. + public void Add(Drawable content) { - var facade = new PlayerGridFacade(); + int index = cellContainer.Count; + + var facade = new Facade(); facadeContainer.Add(facade); - var cell = new Cell(content) { ToggleMaximisationState = toggleMaximisationState }; + var cell = new Cell(index, content) { ToggleMaximisationState = toggleMaximisationState }; cell.SetFacade(facade); cellContainer.Add(cell); } + /// + /// The content added to this grid. + /// + public IEnumerable Content => cellContainer.OrderBy(c => c.FacadeIndex).Select(c => c.Content); + // A depth value that gets decremented every time a new instance is maximised in order to reduce underlaps. private float maximisedInstanceDepth; private void toggleMaximisationState(Cell target) { // Iterate through all cells to ensure only one is maximised at any time. - foreach (var i in cellContainer) + foreach (var i in cellContainer.ToList()) { if (i == target) i.IsMaximised = !i.IsMaximised; @@ -78,7 +90,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate else { // Transfer cell back to its original facade. - i.SetFacade(facadeContainer[cellContainer.IndexOf(target)]); + i.SetFacade(facadeContainer[i.FacadeIndex]); } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs index 1f6e718aa7..c2ac190d40 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs @@ -14,17 +14,28 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { private class Cell : CompositeDrawable { + /// + /// The index of the original facade of this cell. + /// + public readonly int FacadeIndex; + + /// + /// The contained content. + /// + public readonly Drawable Content; + public Action ToggleMaximisationState; public bool IsMaximised; - private PlayerGridFacade facade; + private Facade facade; private bool isTracking = true; - public Cell(Drawable content) + public Cell(int facadeIndex, Drawable content) { - Origin = Anchor.Centre; + FacadeIndex = facadeIndex; - InternalChild = content; + Origin = Anchor.Centre; + InternalChild = Content = content; } protected override void Update() @@ -38,9 +49,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate } } - public void SetFacade([NotNull] PlayerGridFacade newFacade) + /// + /// Makes this cell track a new facade. + /// + public void SetFacade([NotNull] Facade newFacade) { - PlayerGridFacade lastFacade = facade; + Facade lastFacade = facade; facade = newFacade; if (lastFacade == null || lastFacade == newFacade) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs index c565e2fec6..6b363c6040 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs @@ -7,9 +7,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { public partial class PlayerGrid { - private class PlayerGridFacade : Drawable + /// + /// A facade of the grid which is used as a dummy object to store the required position/size of cells. + /// + private class Facade : Drawable { - public PlayerGridFacade() + public Facade() { Anchor = Anchor.Centre; Origin = Anchor.Centre;