Merge pull request #14381 from smoogipoo/match-subscreen-redesign

Update layout of multiplayer room screens
This commit is contained in:
Dean Herbert
2021-08-19 19:36:21 +09:00
committed by GitHub
28 changed files with 936 additions and 1133 deletions

View File

@ -10,11 +10,11 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Online.Rooms.RoomStatuses; using osu.Game.Online.Rooms.RoomStatuses;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
using osu.Game.Users; using osu.Game.Users;
@ -108,12 +108,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
EndDate = { Value = DateTimeOffset.Now }, EndDate = { Value = DateTimeOffset.Now },
}), }),
createDrawableRoom(new Room createDrawableRoom(new Room
{
Name = { Value = "Room 4 (realtime)" },
Status = { Value = new RoomStatusOpen() },
Category = { Value = RoomCategory.Realtime },
}),
createDrawableRoom(new Room
{ {
Name = { Value = "Room 4 (spotlight)" }, Name = { Value = "Room 4 (spotlight)" },
Status = { Value = new RoomStatusOpen() }, Status = { Value = new RoomStatusOpen() },
@ -134,7 +128,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
Name = { Value = "Room with password" }, Name = { Value = "Room with password" },
Status = { Value = new RoomStatusOpen() }, Status = { Value = new RoomStatusOpen() },
Category = { Value = RoomCategory.Realtime }, Type = { Value = MatchType.HeadToHead },
})); }));
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha)); AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha));
@ -159,10 +153,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
})); }));
} }
var drawableRoom = new DrawableRoom(room) { MatchingFilter = true }; return new DrawableLoungeRoom(room) { MatchingFilter = true };
drawableRoom.Action = () => drawableRoom.State = drawableRoom.State == SelectionState.Selected ? SelectionState.NotSelected : SelectionState.Selected;
return drawableRoom;
} }
} }
} }

View File

@ -9,6 +9,7 @@ using osu.Framework.Testing;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Tests.Visual.OnlinePlay;
using osuTK.Input; using osuTK.Input;
@ -150,6 +151,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
private bool checkRoomSelected(Room room) => SelectedRoom.Value == room; private bool checkRoomSelected(Room room) => SelectedRoom.Value == room;
private Room getRoomInFlow(int index) => private Room getRoomInFlow(int index) =>
(container.ChildrenOfType<FillFlowContainer<DrawableRoom>>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room; (container.ChildrenOfType<FillFlowContainer<DrawableLoungeRoom>>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room;
} }
} }

View File

@ -1,55 +0,0 @@
// 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 NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.OnlinePlay.Match.Components;
using osu.Game.Tests.Visual.OnlinePlay;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMatchHeader : OnlinePlayTestScene
{
[SetUp]
public new void Setup() => Schedule(() =>
{
SelectedRoom.Value = new Room
{
Name = { Value = "A very awesome room" },
Host = { Value = new User { Id = 2, Username = "peppy" } },
Playlist =
{
new PlaylistItem
{
Beatmap =
{
Value = new BeatmapInfo
{
Metadata = new BeatmapMetadata
{
Title = "Title",
Artist = "Artist",
AuthorString = "Author",
},
Version = "Version",
Ruleset = new OsuRuleset().RulesetInfo
}
},
RequiredMods =
{
new OsuModDoubleTime(),
new OsuModNoFail(),
new OsuModRelax(),
}
}
}
};
Child = new Header();
});
}
}

View File

@ -236,8 +236,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("join room", () => InputManager.Key(Key.Enter)); AddStep("join room", () => InputManager.Key(Key.Enter));
DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableRoom.PasswordEntryPopover>().FirstOrDefault()) != null); AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password"); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password");
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick()); AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());

View File

@ -9,7 +9,6 @@ using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Tests.Visual.OnlinePlay;
using osuTK.Input; using osuTK.Input;
@ -59,20 +58,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter)); AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType<DrawableRoom.PasswordEntryPopover>().Any()); AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().Any());
AddStep("exit screen", () => Stack.Exit()); AddStep("exit screen", () => Stack.Exit());
AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType<DrawableRoom.PasswordEntryPopover>().Any()); AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().Any());
} }
[Test] [Test]
public void TestJoinRoomWithPassword() public void TestJoinRoomWithPassword()
{ {
DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter)); AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableRoom.PasswordEntryPopover>().FirstOrDefault()) != null); AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password"); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password");
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick()); AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
@ -83,12 +82,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test] [Test]
public void TestJoinRoomWithPasswordViaKeyboardOnly() public void TestJoinRoomWithPasswordViaKeyboardOnly()
{ {
DrawableRoom.PasswordEntryPopover passwordEntryPopover = null; DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true)); AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down)); AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter)); AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableRoom.PasswordEntryPopover>().FirstOrDefault()) != null); AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password"); AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password");
AddStep("press enter", () => InputManager.Key(Key.Enter)); AddStep("press enter", () => InputManager.Key(Key.Enter));

View File

@ -59,23 +59,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for load", () => screen.IsCurrentScreen()); AddUntilStep("wait for load", () => screen.IsCurrentScreen());
} }
[Test]
public void TestSettingValidity()
{
AddAssert("create button not enabled", () => !this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
AddStep("set playlist", () =>
{
SelectedRoom.Value.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
});
});
AddAssert("create button enabled", () => this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
}
[Test] [Test]
public void TestCreatedRoom() public void TestCreatedRoom()
{ {
@ -97,6 +80,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for join", () => Client.Room != null); AddUntilStep("wait for join", () => Client.Room != null);
} }
[Test]
public void TestSettingValidity()
{
AddAssert("create button not enabled", () => !this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
AddStep("set playlist", () =>
{
SelectedRoom.Value.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
});
});
AddAssert("create button enabled", () => this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
}
[Test] [Test]
public void TestStartMatchWhileSpectating() public void TestStartMatchWhileSpectating()
{ {

View File

@ -110,7 +110,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddUntilStep("error not displayed", () => !settings.ErrorText.IsPresent); AddUntilStep("error not displayed", () => !settings.ErrorText.IsPresent);
} }
private class TestRoomSettings : PlaylistsMatchSettingsOverlay private class TestRoomSettings : PlaylistsRoomSettingsOverlay
{ {
public TriangleButton ApplyButton => ((MatchSettings)Settings).ApplyButton; public TriangleButton ApplyButton => ((MatchSettings)Settings).ApplyButton;

View File

@ -19,7 +19,6 @@ using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Tests.Visual.OnlinePlay;
using osu.Game.Users;
using osuTK.Input; using osuTK.Input;
namespace osu.Game.Tests.Visual.Playlists namespace osu.Game.Tests.Visual.Playlists
@ -66,7 +65,7 @@ namespace osu.Game.Tests.Visual.Playlists
{ {
SelectedRoom.Value.RoomID.Value = 1; SelectedRoom.Value.RoomID.Value = 1;
SelectedRoom.Value.Name.Value = "my awesome room"; SelectedRoom.Value.Name.Value = "my awesome room";
SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" }; SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.RecentParticipants.Add(SelectedRoom.Value.Host.Value); SelectedRoom.Value.RecentParticipants.Add(SelectedRoom.Value.Host.Value);
SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
SelectedRoom.Value.Playlist.Add(new PlaylistItem SelectedRoom.Value.Playlist.Add(new PlaylistItem
@ -86,7 +85,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("set room properties", () => AddStep("set room properties", () =>
{ {
SelectedRoom.Value.Name.Value = "my awesome room"; SelectedRoom.Value.Name.Value = "my awesome room";
SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" }; SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.Playlist.Add(new PlaylistItem SelectedRoom.Value.Playlist.Add(new PlaylistItem
{ {
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
@ -96,7 +95,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("move mouse to create button", () => AddStep("move mouse to create button", () =>
{ {
InputManager.MoveMouseTo(this.ChildrenOfType<PlaylistsMatchSettingsOverlay.CreateRoomButton>().Single()); InputManager.MoveMouseTo(this.ChildrenOfType<PlaylistsRoomSettingsOverlay.CreateRoomButton>().Single());
}); });
AddStep("click", () => InputManager.Click(MouseButton.Left)); AddStep("click", () => InputManager.Click(MouseButton.Left));
@ -137,7 +136,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("load room", () => AddStep("load room", () =>
{ {
SelectedRoom.Value.Name.Value = "my awesome room"; SelectedRoom.Value.Name.Value = "my awesome room";
SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" }; SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.Playlist.Add(new PlaylistItem SelectedRoom.Value.Playlist.Add(new PlaylistItem
{ {
Beatmap = { Value = importedSet.Beatmaps[0] }, Beatmap = { Value = importedSet.Beatmaps[0] },
@ -147,7 +146,7 @@ namespace osu.Game.Tests.Visual.Playlists
AddStep("create room", () => AddStep("create room", () =>
{ {
InputManager.MoveMouseTo(match.ChildrenOfType<PlaylistsMatchSettingsOverlay.CreateRoomButton>().Single()); InputManager.MoveMouseTo(match.ChildrenOfType<PlaylistsRoomSettingsOverlay.CreateRoomButton>().Single());
InputManager.Click(MouseButton.Left); InputManager.Click(MouseButton.Left);
}); });

View File

@ -8,6 +8,5 @@ namespace osu.Game.Online.Rooms
// used for osu-web deserialization so names shouldn't be changed. // used for osu-web deserialization so names shouldn't be changed.
Normal, Normal,
Spotlight, Spotlight,
Realtime,
} }
} }

View File

@ -1,32 +1,19 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Input.Bindings;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Components;
@ -35,105 +22,26 @@ using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{ {
public class DrawableRoom : OsuClickableContainer, IStateful<SelectionState>, IFilterable, IHasContextMenu, IHasPopover, IKeyBindingHandler<GlobalAction> public class DrawableRoom : CompositeDrawable
{ {
public const float SELECTION_BORDER_WIDTH = 4; protected const float CORNER_RADIUS = 10;
private const float corner_radius = 10;
private const float transition_duration = 60;
private const float height = 100; private const float height = 100;
public event Action<SelectionState> StateChanged; public readonly Room Room;
protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds();
private Drawable selectionBox;
[Resolved(canBeNull: true)]
private LoungeSubScreen loungeScreen { get; set; }
[Resolved] [Resolved]
private BeatmapManager beatmaps { get; set; } private BeatmapManager beatmaps { get; set; }
[Resolved(canBeNull: true)] protected Container ButtonsContainer { get; private set; }
private Bindable<Room> selectedRoom { get; set; }
[Resolved(canBeNull: true)]
private LoungeSubScreen lounge { get; set; }
public readonly Room Room;
private SelectionState state;
private Sample sampleSelect;
private Sample sampleJoin;
public SelectionState State
{
get => state;
set
{
if (value == state)
return;
state = value;
if (selectionBox != null)
{
if (state == SelectionState.Selected)
selectionBox.FadeIn(transition_duration);
else
selectionBox.FadeOut(transition_duration);
}
StateChanged?.Invoke(State);
}
}
public IEnumerable<string> FilterTerms => new[] { Room.Name.Value };
private bool matchingFilter;
public bool MatchingFilter
{
get => matchingFilter;
set
{
matchingFilter = value;
if (!IsLoaded)
return;
if (matchingFilter)
this.FadeIn(200);
else
Hide();
}
}
private int numberOfAvatars = 7;
public int NumberOfAvatars
{
get => numberOfAvatars;
set
{
numberOfAvatars = value;
if (recentParticipantsList != null)
recentParticipantsList.NumberOfCircles = value;
}
}
private readonly Bindable<MatchType> roomType = new Bindable<MatchType>();
private readonly Bindable<RoomCategory> roomCategory = new Bindable<RoomCategory>(); private readonly Bindable<RoomCategory> roomCategory = new Bindable<RoomCategory>();
private readonly Bindable<bool> hasPassword = new Bindable<bool>();
private RecentParticipantsList recentParticipantsList; private RecentParticipantsList recentParticipantsList;
private RoomSpecialCategoryPill specialCategoryPill; private RoomSpecialCategoryPill specialCategoryPill;
public bool FilteringActive { get; set; }
private PasswordProtectedIcon passwordIcon; private PasswordProtectedIcon passwordIcon;
private EndDateInfo endDateInfo;
private readonly Bindable<bool> hasPassword = new Bindable<bool>();
public DrawableRoom(Room room) public DrawableRoom(Room room)
{ {
@ -143,7 +51,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
Height = height; Height = height;
Masking = true; Masking = true;
CornerRadius = corner_radius + SELECTION_BORDER_WIDTH / 2; CornerRadius = CORNER_RADIUS;
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
{ {
Type = EdgeEffectType.Shadow, Type = EdgeEffectType.Shadow,
@ -153,9 +61,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OverlayColourProvider colours, AudioManager audio) private void load(OverlayColourProvider colours)
{ {
Children = new Drawable[] InternalChildren = new[]
{ {
// This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites. // This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites.
new Box new Box
@ -163,10 +71,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colours.Background5, Colour = colours.Background5,
}, },
new OnlinePlayBackgroundSprite CreateBackground().With(d =>
{ {
RelativeSizeAxes = Axes.Both d.RelativeSizeAxes = Axes.Both;
}, }),
new Container new Container
{ {
Name = @"Room content", Name = @"Room content",
@ -177,7 +85,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Masking = true, Masking = true,
CornerRadius = corner_radius, CornerRadius = CORNER_RADIUS,
Children = new Drawable[] Children = new Drawable[]
{ {
new GridContainer new GridContainer
@ -238,10 +146,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft Origin = Anchor.CentreLeft
}, },
new EndDateInfo endDateInfo = new EndDateInfo
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft Origin = Anchor.CentreLeft,
}, },
} }
}, },
@ -289,13 +197,21 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.X, AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Spacing = new Vector2(5),
Padding = new MarginPadding Padding = new MarginPadding
{ {
Right = 10, Right = 10,
Vertical = 5 Vertical = 20,
}, },
Children = new Drawable[] Children = new Drawable[]
{ {
ButtonsContainer = new Container
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X
},
recentParticipantsList = new RecentParticipantsList recentParticipantsList = new RecentParticipantsList
{ {
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
@ -308,36 +224,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
}, },
}, },
}, },
new StatusColouredContainer(transition_duration)
{
RelativeSizeAxes = Axes.Both,
Child = selectionBox = new Container
{
RelativeSizeAxes = Axes.Both,
Alpha = state == SelectionState.Selected ? 1 : 0,
Masking = true,
CornerRadius = corner_radius,
BorderThickness = SELECTION_BORDER_WIDTH,
BorderColour = Color4.White,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
},
};
sampleSelect = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select");
sampleJoin = audio.Samples.Get($@"UI/{HoverSampleSet.Submit.GetDescription()}-select");
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
return new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent))
{
Model = { Value = Room }
}; };
} }
@ -345,11 +231,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{ {
base.LoadComplete(); base.LoadComplete();
if (matchingFilter)
this.FadeInFromZero(transition_duration);
else
Alpha = 0;
roomCategory.BindTo(Room.Category); roomCategory.BindTo(Room.Category);
roomCategory.BindValueChanged(c => roomCategory.BindValueChanged(c =>
{ {
@ -359,62 +240,39 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
specialCategoryPill.Hide(); specialCategoryPill.Hide();
}, true); }, true);
roomType.BindTo(Room.Type);
roomType.BindValueChanged(t =>
{
endDateInfo.Alpha = t.NewValue == MatchType.Playlists ? 1 : 0;
}, true);
hasPassword.BindTo(Room.HasPassword); hasPassword.BindTo(Room.HasPassword);
hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true); hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true);
} }
public Popover GetPopover() => new PasswordEntryPopover(Room) { JoinRequested = lounge.Join }; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
public MenuItem[] ContextMenuItems => new MenuItem[]
{ {
new OsuMenuItem("Create copy", MenuItemType.Standard, () => return new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent))
{ {
lounge?.Open(Room.DeepClone()); Model = { Value = Room }
})
}; };
public bool OnPressed(GlobalAction action)
{
if (selectedRoom.Value != Room)
return false;
switch (action)
{
case GlobalAction.Select:
TriggerClick();
return true;
} }
return false; private int numberOfAvatars = 7;
}
public void OnReleased(GlobalAction action) public int NumberOfAvatars
{ {
} get => numberOfAvatars;
set
protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected || child is HoverSounds;
protected override bool OnClick(ClickEvent e)
{ {
if (Room != selectedRoom.Value) numberOfAvatars = value;
{
sampleSelect?.Play(); if (recentParticipantsList != null)
selectedRoom.Value = Room; recentParticipantsList.NumberOfCircles = value;
return true; }
} }
if (Room.HasPassword.Value) protected virtual Drawable CreateBackground() => new OnlinePlayBackgroundSprite();
{
sampleJoin?.Play();
this.ShowPopover();
return true;
}
sampleJoin?.Play();
lounge?.Join(Room, null);
return base.OnClick(e);
}
private class RoomNameText : OsuSpriteText private class RoomNameText : OsuSpriteText
{ {
@ -500,52 +358,5 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
}; };
} }
} }
public class PasswordEntryPopover : OsuPopover
{
private readonly Room room;
public Action<Room, string> JoinRequested;
public PasswordEntryPopover(Room room)
{
this.room = room;
}
private OsuPasswordTextBox passwordTextbox;
[BackgroundDependencyLoader]
private void load()
{
Child = new FillFlowContainer
{
Margin = new MarginPadding(10),
Spacing = new Vector2(5),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
passwordTextbox = new OsuPasswordTextBox
{
Width = 200,
},
new TriangleButton
{
Width = 80,
Text = "Join Room",
Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text)
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox));
passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text);
}
}
} }
} }

View File

@ -15,7 +15,6 @@ using osu.Framework.Input.Events;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Extensions; using osu.Game.Extensions;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osuTK; using osuTK;
@ -26,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{ {
private readonly IBindableList<Room> rooms = new BindableList<Room>(); private readonly IBindableList<Room> rooms = new BindableList<Room>();
private readonly FillFlowContainer<DrawableRoom> roomFlow; private readonly FillFlowContainer<DrawableLoungeRoom> roomFlow;
public IReadOnlyList<DrawableRoom> Rooms => roomFlow.FlowingChildren.Cast<DrawableRoom>().ToArray(); public IReadOnlyList<DrawableRoom> Rooms => roomFlow.FlowingChildren.Cast<DrawableRoom>().ToArray();
@ -56,7 +55,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Child = roomFlow = new FillFlowContainer<DrawableRoom> Child = roomFlow = new FillFlowContainer<DrawableLoungeRoom>
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
@ -74,16 +73,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
rooms.BindTo(roomManager.Rooms); rooms.BindTo(roomManager.Rooms);
Filter?.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true); Filter?.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true);
selectedRoom.BindValueChanged(selection =>
{
updateSelection();
}, true);
} }
private void updateSelection() =>
roomFlow.Children.ForEach(r => r.State = r.Room == selectedRoom.Value ? SelectionState.Selected : SelectionState.NotSelected);
private void applyFilterCriteria(FilterCriteria criteria) private void applyFilterCriteria(FilterCriteria criteria)
{ {
roomFlow.Children.ForEach(r => roomFlow.Children.ForEach(r =>
@ -122,22 +113,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{ {
foreach (var room in rooms) foreach (var room in rooms)
{ {
roomFlow.Add(new DrawableRoom(room)); roomFlow.Add(new DrawableLoungeRoom(room));
} }
applyFilterCriteria(Filter?.Value); applyFilterCriteria(Filter?.Value);
updateSelection();
} }
private void removeRooms(IEnumerable<Room> rooms) private void removeRooms(IEnumerable<Room> rooms)
{ {
foreach (var r in rooms) foreach (var r in rooms)
{ {
var toRemove = roomFlow.Single(d => d.Room == r); roomFlow.RemoveAll(d => d.Room == r);
toRemove.Action = null;
roomFlow.Remove(toRemove);
// selection may have a lease due to being in a sub screen. // selection may have a lease due to being in a sub screen.
if (!selectedRoom.Disabled) if (!selectedRoom.Disabled)

View File

@ -0,0 +1,226 @@
// 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.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Input.Bindings;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay.Lounge
{
/// <summary>
/// A <see cref="DrawableRoom"/> with lounge-specific interactions such as selection and hover sounds.
/// </summary>
public class DrawableLoungeRoom : DrawableRoom, IFilterable, IHasContextMenu, IHasPopover, IKeyBindingHandler<GlobalAction>
{
private const float transition_duration = 60;
private const float selection_border_width = 4;
[Resolved(canBeNull: true)]
private LoungeSubScreen lounge { get; set; }
[Resolved(canBeNull: true)]
private Bindable<Room> selectedRoom { get; set; }
private Sample sampleSelect;
private Sample sampleJoin;
private Drawable selectionBox;
public DrawableLoungeRoom(Room room)
: base(room)
{
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleSelect = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select");
sampleJoin = audio.Samples.Get($@"UI/{HoverSampleSet.Submit.GetDescription()}-select");
AddRangeInternal(new Drawable[]
{
new StatusColouredContainer(transition_duration)
{
RelativeSizeAxes = Axes.Both,
Child = selectionBox = new Container
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
Masking = true,
CornerRadius = CORNER_RADIUS,
BorderThickness = selection_border_width,
BorderColour = Color4.White,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
},
new HoverSounds()
});
}
protected override void LoadComplete()
{
base.LoadComplete();
if (matchingFilter)
this.FadeInFromZero(transition_duration);
else
Alpha = 0;
selectedRoom.BindValueChanged(updateSelectedRoom, true);
}
private void updateSelectedRoom(ValueChangedEvent<Room> selected)
{
if (selected.NewValue == Room)
selectionBox.FadeIn(transition_duration);
else
selectionBox.FadeOut(transition_duration);
}
public bool FilteringActive { get; set; }
public IEnumerable<string> FilterTerms => new[] { Room.Name.Value };
private bool matchingFilter;
public bool MatchingFilter
{
get => matchingFilter;
set
{
matchingFilter = value;
if (!IsLoaded)
return;
if (matchingFilter)
this.FadeIn(200);
else
Hide();
}
}
public Popover GetPopover() => new PasswordEntryPopover(Room) { JoinRequested = lounge.Join };
public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuMenuItem("Create copy", MenuItemType.Standard, () =>
{
lounge?.Open(Room.DeepClone());
})
};
public bool OnPressed(GlobalAction action)
{
if (selectedRoom.Value != Room)
return false;
switch (action)
{
case GlobalAction.Select:
TriggerClick();
return true;
}
return false;
}
public void OnReleased(GlobalAction action)
{
}
protected override bool ShouldBeConsideredForInput(Drawable child) => selectedRoom.Value == Room || child is HoverSounds;
protected override bool OnClick(ClickEvent e)
{
if (Room != selectedRoom.Value)
{
sampleSelect?.Play();
selectedRoom.Value = Room;
return true;
}
if (Room.HasPassword.Value)
{
sampleJoin?.Play();
this.ShowPopover();
return true;
}
sampleJoin?.Play();
lounge?.Join(Room, null);
return base.OnClick(e);
}
public class PasswordEntryPopover : OsuPopover
{
private readonly Room room;
public Action<Room, string> JoinRequested;
public PasswordEntryPopover(Room room)
{
this.room = room;
}
private OsuPasswordTextBox passwordTextbox;
[BackgroundDependencyLoader]
private void load()
{
Child = new FillFlowContainer
{
Margin = new MarginPadding(10),
Spacing = new Vector2(5),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
passwordTextbox = new OsuPasswordTextBox
{
Width = 200,
},
new TriangleButton
{
Width = 80,
Text = "Join Room",
Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text)
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox));
passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text);
}
}
}
}

View File

@ -1,48 +0,0 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Screens.OnlinePlay.Playlists;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Match.Components
{
public class Footer : CompositeDrawable
{
public const float HEIGHT = 50;
public Action OnStart;
private readonly Drawable background;
public Footer()
{
RelativeSizeAxes = Axes.X;
Height = HEIGHT;
InternalChildren = new[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
new PlaylistsReadyButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(600, 50),
Action = () => OnStart?.Invoke()
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = Color4Extensions.FromHex(@"28242d");
}
}
}

View File

@ -1,79 +0,0 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users.Drawables;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Match.Components
{
public class Header : OnlinePlayComposite
{
public const float HEIGHT = 50;
private UpdateableAvatar avatar;
private LinkFlowContainer hostText;
public Header()
{
RelativeSizeAxes = Axes.X;
Height = HEIGHT;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
avatar = new UpdateableAvatar
{
Size = new Vector2(50),
Masking = true,
CornerRadius = 10,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Font = OsuFont.GetFont(size: 30),
Current = { BindTarget = RoomName }
},
hostText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 20))
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
}
}
}
}
};
Host.BindValueChanged(host =>
{
avatar.User = host.NewValue;
hostText.Clear();
if (host.NewValue != null)
{
hostText.AddText("hosted by ");
hostText.AddUserLink(host.NewValue, s => s.Font = s.Font.With(weight: FontWeight.SemiBold));
}
}, true);
}
}
}

View File

@ -14,10 +14,10 @@ using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay.Match.Components namespace osu.Game.Screens.OnlinePlay.Match.Components
{ {
public abstract class MatchSettingsOverlay : FocusedOverlayContainer, IKeyBindingHandler<GlobalAction> public abstract class RoomSettingsOverlay : FocusedOverlayContainer, IKeyBindingHandler<GlobalAction>
{ {
protected const float TRANSITION_DURATION = 350; protected const float TRANSITION_DURATION = 350;
protected const float FIELD_PADDING = 45; protected const float FIELD_PADDING = 25;
protected OnlinePlayComposite Settings { get; set; } protected OnlinePlayComposite Settings { get; set; }
@ -27,11 +27,16 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
protected abstract bool IsLoading { get; } protected abstract bool IsLoading { get; }
protected RoomSettingsOverlay()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
CornerRadius = 10;
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Masking = true;
Add(Settings = CreateSettings()); Add(Settings = CreateSettings());
} }
@ -43,12 +48,14 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
{ {
base.PopIn(); base.PopIn();
Settings.MoveToY(0, TRANSITION_DURATION, Easing.OutQuint); Settings.MoveToY(0, TRANSITION_DURATION, Easing.OutQuint);
Settings.FadeIn(TRANSITION_DURATION / 2);
} }
protected override void PopOut() protected override void PopOut()
{ {
base.PopOut(); base.PopOut();
Settings.MoveToY(-1, TRANSITION_DURATION, Easing.InSine); Settings.MoveToY(-1, TRANSITION_DURATION, Easing.InSine);
Settings.Delay(TRANSITION_DURATION / 2).FadeOut(TRANSITION_DURATION / 2);
} }
public bool OnPressed(GlobalAction action) public bool OnPressed(GlobalAction action)

View File

@ -0,0 +1,64 @@
// 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 JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match.Components;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Match
{
public class DrawableMatchRoom : DrawableRoom
{
public Action OnEdit;
[Resolved]
private IAPIProvider api { get; set; }
private readonly IBindable<User> host = new Bindable<User>();
private readonly bool allowEdit;
[CanBeNull]
private Drawable editButton;
public DrawableMatchRoom(Room room, bool allowEdit = true)
: base(room)
{
this.allowEdit = allowEdit;
host.BindTo(room.Host);
}
[BackgroundDependencyLoader]
private void load()
{
if (allowEdit)
{
ButtonsContainer.Add(editButton = new PurpleTriangleButton
{
RelativeSizeAxes = Axes.Y,
Size = new Vector2(100, 1),
Text = "Edit",
Action = () => OnEdit?.Invoke()
});
}
}
protected override void LoadComplete()
{
base.LoadComplete();
if (editButton != null)
host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true);
}
protected override Drawable CreateBackground() => new RoomBackgroundSprite();
}
}

View File

@ -0,0 +1,33 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps.Drawables;
namespace osu.Game.Screens.OnlinePlay.Match
{
public class RoomBackgroundSprite : RoomSubScreenComposite
{
protected readonly BeatmapSetCoverType BeatmapSetCoverType;
private UpdateableBeatmapBackgroundSprite sprite;
public RoomBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover)
{
BeatmapSetCoverType = beatmapSetCoverType;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = sprite = new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both };
}
protected override void LoadComplete()
{
base.LoadComplete();
SelectedItem.BindValueChanged(item => sprite.Beatmap.Value = item.NewValue?.Beatmap.Value, true);
}
}
}

View File

@ -31,8 +31,6 @@ namespace osu.Game.Screens.OnlinePlay.Match
public override bool DisallowExternalBeatmapRulesetChanges => true; public override bool DisallowExternalBeatmapRulesetChanges => true;
private readonly ModSelectOverlay userModsSelectOverlay;
/// <summary> /// <summary>
/// A container that provides controls for selection of user mods. /// A container that provides controls for selection of user mods.
/// This will be shown/hidden automatically when applicable. /// This will be shown/hidden automatically when applicable.
@ -46,6 +44,8 @@ namespace osu.Game.Screens.OnlinePlay.Match
/// </summary> /// </summary>
protected readonly Bindable<IReadOnlyList<Mod>> UserMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>()); protected readonly Bindable<IReadOnlyList<Mod>> UserMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
protected readonly IBindable<long?> RoomId = new Bindable<long?>();
[Resolved] [Resolved]
private MusicController music { get; set; } private MusicController music { get; set; }
@ -58,55 +58,184 @@ namespace osu.Game.Screens.OnlinePlay.Match
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated; private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
[Cached] [Cached]
protected OnlinePlayBeatmapAvailabilityTracker BeatmapAvailabilityTracker { get; } protected OnlinePlayBeatmapAvailabilityTracker BeatmapAvailabilityTracker { get; private set; }
protected IBindable<BeatmapAvailability> BeatmapAvailability => BeatmapAvailabilityTracker.Availability; protected IBindable<BeatmapAvailability> BeatmapAvailability => BeatmapAvailabilityTracker.Availability;
protected RoomSubScreen() public readonly Room Room;
private readonly bool allowEdit;
private ModSelectOverlay userModsSelectOverlay;
private RoomSettingsOverlay settingsOverlay;
private Drawable mainContent;
/// <summary>
/// Creates a new <see cref="RoomSubScreen"/>.
/// </summary>
/// <param name="room">The <see cref="Room"/>.</param>
/// <param name="allowEdit">Whether to allow editing room settings post-creation.</param>
protected RoomSubScreen(Room room, bool allowEdit = true)
{ {
Room = room;
this.allowEdit = allowEdit;
Padding = new MarginPadding { Top = Header.HEIGHT }; Padding = new MarginPadding { Top = Header.HEIGHT };
AddRangeInternal(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex(@"3e3a44") // This is super temporary.
},
BeatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker BeatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker
{ {
SelectedItem = { BindTarget = SelectedItem } SelectedItem = { BindTarget = SelectedItem }
};
RoomId.BindTo(room.RoomID);
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
sampleStart = audio.Samples.Get(@"SongSelect/confirm-selection");
InternalChildren = new Drawable[]
{
BeatmapAvailabilityTracker,
new GridContainer
{
RelativeSizeAxes = Axes.Both,
RowDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.Absolute, 50)
},
Content = new[]
{
// Padded main content (drawable room + main content)
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Horizontal = WaveOverlayContainer.WIDTH_PADDING,
Bottom = 30
},
Children = new[]
{
mainContent = new GridContainer
{
RelativeSizeAxes = Axes.Both,
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.Absolute, 10)
},
Content = new[]
{
new Drawable[]
{
new DrawableMatchRoom(Room, allowEdit)
{
OnEdit = () => settingsOverlay.Show()
}
},
null,
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Children = new[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 10,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex(@"3e3a44") // Temporary.
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(20),
Child = CreateMainContent(),
}, },
new Container new Container
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Depth = float.MinValue,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING },
Child = userModsSelectOverlay = new UserModSelectOverlay Child = userModsSelectOverlay = new UserModSelectOverlay
{ {
SelectedMods = { BindTarget = UserMods }, SelectedMods = { BindTarget = UserMods },
IsValidMod = _ => false IsValidMod = _ => false
} }
}, },
});
} }
}
protected override void ClearInternal(bool disposeChildren = true) => }
throw new InvalidOperationException($"{nameof(RoomSubScreen)}'s children should not be cleared as it will remove required components"); }
},
[BackgroundDependencyLoader] new Container
private void load(AudioManager audio)
{ {
sampleStart = audio.Samples.Get(@"SongSelect/confirm-selection"); RelativeSizeAxes = Axes.Both,
// Resolves 1px masking errors between the settings overlay and the room panel.
Padding = new MarginPadding(-1),
Child = settingsOverlay = CreateRoomSettingsOverlay()
}
},
},
},
// Footer
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex(@"28242d") // Temporary.
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(5),
Child = CreateFooter()
},
}
}
}
}
}
};
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
RoomId.BindValueChanged(id =>
{
if (id.NewValue == null)
{
// A new room is being created.
// The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed.
mainContent.Hide();
settingsOverlay.Show();
}
else
{
mainContent.Show();
settingsOverlay.Hide();
}
}, true);
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged)); SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged));
managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy(); managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy();
@ -117,12 +246,25 @@ namespace osu.Game.Screens.OnlinePlay.Match
public override bool OnBackButton() public override bool OnBackButton()
{ {
if (Room.RoomID.Value == null)
{
// room has not been created yet; exit immediately.
settingsOverlay.Hide();
return base.OnBackButton();
}
if (userModsSelectOverlay.State.Value == Visibility.Visible) if (userModsSelectOverlay.State.Value == Visibility.Visible)
{ {
userModsSelectOverlay.Hide(); userModsSelectOverlay.Hide();
return true; return true;
} }
if (settingsOverlay.State.Value == Visibility.Visible)
{
settingsOverlay.Hide();
return true;
}
return base.OnBackButton(); return base.OnBackButton();
} }
@ -257,6 +399,21 @@ namespace osu.Game.Screens.OnlinePlay.Match
track.Looping = false; track.Looping = false;
} }
/// <summary>
/// Creates the main centred content.
/// </summary>
protected abstract Drawable CreateMainContent();
/// <summary>
/// Creates the footer content.
/// </summary>
protected abstract Drawable CreateFooter();
/// <summary>
/// Creates the room settings overlay.
/// </summary>
protected abstract RoomSettingsOverlay CreateRoomSettingsOverlay();
private class UserModSelectOverlay : LocalPlayerModSelectOverlay private class UserModSelectOverlay : LocalPlayerModSelectOverlay
{ {
} }

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.OnlinePlay.Match.Components;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{ {
@ -35,6 +36,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Spacing = new Vector2(5),
Children = new Drawable[] Children = new Drawable[]
{ {
beatmapPanelContainer = new Container beatmapPanelContainer = new Container

View File

@ -2,18 +2,13 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{ {
public class MultiplayerMatchFooter : CompositeDrawable public class MultiplayerMatchFooter : CompositeDrawable
{ {
public const float HEIGHT = 50;
private const float ready_button_width = 600; private const float ready_button_width = 600;
private const float spectate_button_width = 200; private const float spectate_button_width = 200;
@ -27,19 +22,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
set => spectateButton.OnSpectateClick = value; set => spectateButton.OnSpectateClick = value;
} }
private readonly Drawable background;
private readonly MultiplayerReadyButton readyButton; private readonly MultiplayerReadyButton readyButton;
private readonly MultiplayerSpectateButton spectateButton; private readonly MultiplayerSpectateButton spectateButton;
public MultiplayerMatchFooter() public MultiplayerMatchFooter()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.Both;
Height = HEIGHT;
InternalChildren = new[] InternalChild = new GridContainer
{
background = new Box { RelativeSizeAxes = Axes.Both },
new GridContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Content = new[] Content = new[]
@ -63,18 +53,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{ {
new Dimension(), new Dimension(),
new Dimension(maxSize: spectate_button_width), new Dimension(maxSize: spectate_button_width),
new Dimension(GridSizeMode.Absolute, 10), new Dimension(GridSizeMode.Absolute, 5),
new Dimension(maxSize: ready_button_width), new Dimension(maxSize: ready_button_width),
new Dimension() new Dimension()
} }
}
}; };
} }
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = Color4Extensions.FromHex(@"28242d");
}
} }
} }

View File

@ -1,106 +0,0 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
using osu.Game.Screens.OnlinePlay.Match.Components;
using osu.Game.Users.Drawables;
using osuTK;
using FontWeight = osu.Game.Graphics.FontWeight;
using OsuColour = osu.Game.Graphics.OsuColour;
using OsuFont = osu.Game.Graphics.OsuFont;
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{
public class MultiplayerMatchHeader : OnlinePlayComposite
{
public const float HEIGHT = 50;
public Action OpenSettings;
private UpdateableAvatar avatar;
private LinkFlowContainer hostText;
private Button openSettingsButton;
[Resolved]
private IAPIProvider api { get; set; }
public MultiplayerMatchHeader()
{
RelativeSizeAxes = Axes.X;
Height = HEIGHT;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
avatar = new UpdateableAvatar
{
Size = new Vector2(50),
Masking = true,
CornerRadius = 10,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Font = OsuFont.GetFont(size: 30),
Current = { BindTarget = RoomName }
},
hostText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 20))
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
}
}
}
}
},
openSettingsButton = new PurpleTriangleButton
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Size = new Vector2(150, HEIGHT),
Text = "Open settings",
Action = () => OpenSettings?.Invoke(),
Alpha = 0
}
};
Host.BindValueChanged(host =>
{
avatar.User = host.NewValue;
hostText.Clear();
if (host.NewValue != null)
{
hostText.AddText("hosted by ");
hostText.AddUserLink(host.NewValue, s => s.Font = s.Font.With(weight: FontWeight.SemiBold));
}
openSettingsButton.Alpha = host.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0;
}, true);
}
}
}

View File

@ -26,7 +26,7 @@ using osuTK;
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{ {
public class MultiplayerMatchSettingsOverlay : MatchSettingsOverlay public class MultiplayerMatchSettingsOverlay : RoomSettingsOverlay
{ {
private MatchSettings settings; private MatchSettings settings;

View File

@ -50,7 +50,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
protected override Room CreateNewRoom() => new Room protected override Room CreateNewRoom() => new Room
{ {
Name = { Value = $"{api.LocalUser}'s awesome room" }, Name = { Value = $"{api.LocalUser}'s awesome room" },
Category = { Value = RoomCategory.Realtime },
Type = { Value = MatchType.HeadToHead }, Type = { Value = MatchType.HeadToHead },
}; };

View File

@ -42,8 +42,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
public override string ShortTitle => "room"; public override string ShortTitle => "room";
public readonly Room Room;
[Resolved] [Resolved]
private MultiplayerClient client { get; set; } private MultiplayerClient client { get; set; }
@ -58,54 +56,49 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
[CanBeNull] [CanBeNull]
private IDisposable readyClickOperation; private IDisposable readyClickOperation;
private GridContainer mainContent;
private MultiplayerMatchSettingsOverlay settingsOverlay;
public MultiplayerMatchSubScreen(Room room) public MultiplayerMatchSubScreen(Room room)
: base(room)
{ {
Room = room;
Title = room.RoomID.Value == null ? "New room" : room.Name.Value; Title = room.RoomID.Value == null ? "New room" : room.Name.Value;
Activity.Value = new UserActivity.InLobby(room); Activity.Value = new UserActivity.InLobby(room);
} }
[BackgroundDependencyLoader] protected override void LoadComplete()
private void load()
{ {
AddRangeInternal(new Drawable[] base.LoadComplete();
SelectedItem.BindTo(client.CurrentMatchPlayingItem);
BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true);
UserMods.BindValueChanged(onUserModsChanged);
client.LoadRequested += onLoadRequested;
client.RoomUpdated += onRoomUpdated;
isConnected.BindTo(client.IsConnected);
isConnected.BindValueChanged(connected =>
{ {
mainContent = new GridContainer if (!connected.NewValue)
handleRoomLost();
}, true);
currentRoom.BindValueChanged(room =>
{ {
RelativeSizeAxes = Axes.Both, if (room.NewValue == null)
Content = new[]
{ {
new Drawable[] // the room has gone away.
{ // this could mean something happened during the join process, or an external connection issue occurred.
new Container // one specific scenario is where the underlying room is created, but the signalr server returns an error during the join process. this triggers a PartRoom operation (see https://github.com/ppy/osu/blob/7654df94f6f37b8382be7dfcb4f674e03bd35427/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs#L97)
{ handleRoomLost();
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Horizontal = HORIZONTAL_OVERFLOW_PADDING + 55,
Vertical = 20
},
Child = new GridContainer
{
RelativeSizeAxes = Axes.Both,
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(),
},
Content = new[]
{
new Drawable[]
{
new MultiplayerMatchHeader
{
OpenSettings = () => settingsOverlay.Show()
} }
}, }, true);
}
protected override Drawable CreateMainContent() => new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[] new Drawable[]
{ {
new Container new Container
@ -148,27 +141,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
// Spacer // Spacer
null, null,
// Main right column // Main right column
new FillFlowContainer new GridContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.Both,
AutoSizeAxes = Axes.Y, Content = new[]
Children = new[]
{ {
new FillFlowContainer new Drawable[] { new OverlinedHeader("Beatmap") },
new Drawable[] { new BeatmapSelectionControl { RelativeSizeAxes = Axes.X } },
new[]
{ {
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new OverlinedHeader("Beatmap"),
new BeatmapSelectionControl { RelativeSizeAxes = Axes.X }
}
},
UserModsSection = new FillFlowContainer UserModsSection = new FillFlowContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 10 }, Margin = new MarginPadding { Top = 10 },
Alpha = 0,
Children = new Drawable[] Children = new Drawable[]
{ {
new OverlinedHeader("Extra mods"), new OverlinedHeader("Extra mods"),
@ -195,102 +182,37 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
Scale = new Vector2(0.8f), Scale = new Vector2(0.8f),
}, },
} }
} },
}
}
}
}
}
}
}
} }
}, },
new Drawable[] },
{ new Drawable[] { new OverlinedHeader("Chat") { Margin = new MarginPadding { Vertical = 5 }, }, },
new GridContainer new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } }
{ },
RelativeSizeAxes = Axes.Both,
RowDimensions = new[] RowDimensions = new[]
{ {
new Dimension(GridSizeMode.AutoSize) new Dimension(GridSizeMode.AutoSize),
}, new Dimension(GridSizeMode.AutoSize),
Content = new[] new Dimension(GridSizeMode.AutoSize),
{ new Dimension(GridSizeMode.AutoSize),
new Drawable[] { new OverlinedHeader("Chat") }, new Dimension(),
new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } }
}
}
} }
}, },
} }
} }
}
}
}, },
new Drawable[] },
{ };
new MultiplayerMatchFooter
protected override Drawable CreateFooter() => new MultiplayerMatchFooter
{ {
OnReadyClick = onReadyClick, OnReadyClick = onReadyClick,
OnSpectateClick = onSpectateClick OnSpectateClick = onSpectateClick
} };
}
},
RowDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.AutoSize),
}
},
settingsOverlay = new MultiplayerMatchSettingsOverlay
{
RelativeSizeAxes = Axes.Both,
State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden }
}
});
if (client.Room == null) protected override RoomSettingsOverlay CreateRoomSettingsOverlay() => new MultiplayerMatchSettingsOverlay();
{
// A new room is being created.
// The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed.
mainContent.Hide();
settingsOverlay.State.BindValueChanged(visibility =>
{
if (visibility.NewValue == Visibility.Hidden)
mainContent.Show();
}, true);
}
}
protected override void LoadComplete()
{
base.LoadComplete();
SelectedItem.BindTo(client.CurrentMatchPlayingItem);
BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true);
UserMods.BindValueChanged(onUserModsChanged);
client.LoadRequested += onLoadRequested;
client.RoomUpdated += onRoomUpdated;
isConnected.BindTo(client.IsConnected);
isConnected.BindValueChanged(connected =>
{
if (!connected.NewValue)
handleRoomLost();
}, true);
currentRoom.BindValueChanged(room =>
{
if (room.NewValue == null)
{
// the room has gone away.
// this could mean something happened during the join process, or an external connection issue occurred.
// one specific scenario is where the underlying room is created, but the signalr server returns an error during the join process. this triggers a PartRoom operation (see https://github.com/ppy/osu/blob/7654df94f6f37b8382be7dfcb4f674e03bd35427/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs#L97)
handleRoomLost();
}
}, true);
}
protected override void UpdateMods() protected override void UpdateMods()
{ {
@ -308,23 +230,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
private bool exitConfirmed; private bool exitConfirmed;
public override bool OnBackButton()
{
if (client.Room == null)
{
// room has not been created yet; exit immediately.
return base.OnBackButton();
}
if (settingsOverlay.State.Value == Visibility.Visible)
{
settingsOverlay.Hide();
return true;
}
return base.OnBackButton();
}
public override bool OnExiting(IScreen next) public override bool OnExiting(IScreen next)
{ {
// the room may not be left immediately after a disconnection due to async flow, // the room may not be left immediately after a disconnection due to async flow,
@ -508,19 +413,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
} }
} }
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (client != null)
{
client.RoomUpdated -= onRoomUpdated;
client.LoadRequested -= onLoadRequested;
}
modSettingChangeTracker?.Dispose();
}
public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset) public void PresentBeatmap(WorkingBeatmap beatmap, RulesetInfo ruleset)
{ {
if (!this.IsCurrentScreen()) if (!this.IsCurrentScreen())
@ -536,5 +428,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
this.Push(new MultiplayerMatchSongSelect(beatmap, ruleset)); this.Push(new MultiplayerMatchSongSelect(beatmap, ruleset));
} }
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (client != null)
{
client.RoomUpdated -= onRoomUpdated;
client.LoadRequested -= onLoadRequested;
}
modSettingChangeTracker?.Dispose();
}
} }
} }

View File

@ -23,12 +23,6 @@ namespace osu.Game.Screens.OnlinePlay
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
} }
public const float X_SHIFT = 200;
public const double X_MOVE_DURATION = 800;
public const double RESUME_TRANSITION_DELAY = DISAPPEAR_DURATION / 2;
public const double APPEAR_DURATION = 800; public const double APPEAR_DURATION = 800;
public const double DISAPPEAR_DURATION = 500; public const double DISAPPEAR_DURATION = 500;
@ -36,28 +30,23 @@ namespace osu.Game.Screens.OnlinePlay
public override void OnEntering(IScreen last) public override void OnEntering(IScreen last)
{ {
this.FadeInFromZero(APPEAR_DURATION, Easing.OutQuint); this.FadeInFromZero(APPEAR_DURATION, Easing.OutQuint);
this.FadeInFromZero(APPEAR_DURATION, Easing.OutQuint);
this.MoveToX(X_SHIFT).MoveToX(0, X_MOVE_DURATION, Easing.OutQuint);
} }
public override bool OnExiting(IScreen next) public override bool OnExiting(IScreen next)
{ {
this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint);
this.MoveToX(X_SHIFT, X_MOVE_DURATION, Easing.OutQuint);
return false; return false;
} }
public override void OnResuming(IScreen last) public override void OnResuming(IScreen last)
{ {
this.Delay(RESUME_TRANSITION_DELAY).FadeIn(APPEAR_DURATION, Easing.OutQuint); this.FadeIn(APPEAR_DURATION, Easing.OutQuint);
this.MoveToX(0, X_MOVE_DURATION, Easing.OutQuint);
} }
public override void OnSuspending(IScreen next) public override void OnSuspending(IScreen next)
{ {
this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint); this.FadeOut(DISAPPEAR_DURATION, Easing.OutQuint);
this.MoveToX(-X_SHIFT, X_MOVE_DURATION, Easing.OutQuint);
} }
public override string ToString() => Title; public override string ToString() => Title;

View File

@ -0,0 +1,32 @@
// 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 osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Playlists
{
public class PlaylistsRoomFooter : CompositeDrawable
{
public Action OnStart;
public PlaylistsRoomFooter()
{
RelativeSizeAxes = Axes.Both;
InternalChildren = new[]
{
new PlaylistsReadyButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Size = new Vector2(600, 1),
Action = () => OnStart?.Invoke()
}
};
}
}
}

View File

@ -22,7 +22,7 @@ using osuTK;
namespace osu.Game.Screens.OnlinePlay.Playlists namespace osu.Game.Screens.OnlinePlay.Playlists
{ {
public class PlaylistsMatchSettingsOverlay : MatchSettingsOverlay public class PlaylistsRoomSettingsOverlay : RoomSettingsOverlay
{ {
public Action EditPlaylist; public Action EditPlaylist;
@ -193,7 +193,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
Child = new GridContainer Child = new GridContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 500, Height = 448,
Content = new[] Content = new[]
{ {
new Drawable[] new Drawable[]

View File

@ -20,7 +20,6 @@ using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
using osu.Game.Users; using osu.Game.Users;
using osuTK; using osuTK;
using Footer = osu.Game.Screens.OnlinePlay.Match.Components.Footer;
namespace osu.Game.Screens.OnlinePlay.Playlists namespace osu.Game.Screens.OnlinePlay.Playlists
{ {
@ -30,21 +29,16 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
public override string ShortTitle => "playlist"; public override string ShortTitle => "playlist";
[Resolved(typeof(Room), nameof(Room.RoomID))] [Resolved]
private Bindable<long?> roomId { get; set; } private IAPIProvider api { get; set; }
[Resolved(typeof(Room), nameof(Room.Playlist))]
private BindableList<PlaylistItem> playlist { get; set; }
private readonly IBindable<bool> isIdle = new BindableBool(); private readonly IBindable<bool> isIdle = new BindableBool();
private MatchSettingsOverlay settingsOverlay;
private MatchLeaderboard leaderboard; private MatchLeaderboard leaderboard;
private OverlinedHeader participantsHeader;
private GridContainer mainContent;
private SelectionPollingComponent selectionPollingComponent; private SelectionPollingComponent selectionPollingComponent;
public PlaylistsRoomSubScreen(Room room) public PlaylistsRoomSubScreen(Room room)
: base(room, false) // Editing is temporarily not allowed.
{ {
Title = room.RoomID.Value == null ? "New playlist" : room.Name.Value; Title = room.RoomID.Value == null ? "New playlist" : room.Name.Value;
Activity.Value = new UserActivity.InLobby(room); Activity.Value = new UserActivity.InLobby(room);
@ -56,60 +50,26 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
if (idleTracker != null) if (idleTracker != null)
isIdle.BindTo(idleTracker.IsIdle); isIdle.BindTo(idleTracker.IsIdle);
AddRangeInternal(new Drawable[] AddInternal(selectionPollingComponent = new SelectionPollingComponent());
{
selectionPollingComponent = new SelectionPollingComponent(),
mainContent = new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Horizontal = 105,
Vertical = 20
},
Child = new GridContainer
{
RelativeSizeAxes = Axes.Both,
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.AutoSize),
new Dimension(),
},
Content = new[]
{
new Drawable[] { new Match.Components.Header() },
new Drawable[]
{
participantsHeader = new OverlinedHeader("Participants")
{
ShowLine = false
} }
},
new Drawable[] protected override void LoadComplete()
{ {
new Container base.LoadComplete();
isIdle.BindValueChanged(_ => updatePollingRate(), true);
RoomId.BindValueChanged(id =>
{ {
RelativeSizeAxes = Axes.X, if (id.NewValue != null)
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 5 },
Child = new ParticipantsDisplay(Direction.Horizontal)
{ {
Details = { BindTarget = participantsHeader.Details } // Set the first playlist item.
// This is scheduled since updating the room and playlist may happen in an arbitrary order (via Room.CopyFrom()).
Schedule(() => SelectedItem.Value = Room.Playlist.FirstOrDefault());
} }
}, true);
} }
},
new Drawable[] protected override Drawable CreateMainContent() => new GridContainer
{
new GridContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Content = new[] Content = new[]
@ -131,12 +91,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
new DrawableRoomPlaylistWithResults new DrawableRoomPlaylistWithResults
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Items = { BindTarget = playlist }, Items = { BindTarget = Room.Playlist },
SelectedItem = { BindTarget = SelectedItem }, SelectedItem = { BindTarget = SelectedItem },
RequestShowResults = item => RequestShowResults = item =>
{ {
Debug.Assert(roomId.Value != null); Debug.Assert(RoomId.Value != null);
ParentScreen?.Push(new PlaylistsResultsScreen(null, roomId.Value.Value, item, false)); ParentScreen?.Push(new PlaylistsResultsScreen(null, RoomId.Value.Value, item, false));
} }
} }
}, },
@ -160,6 +120,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Alpha = 0,
Margin = new MarginPadding { Bottom = 10 }, Margin = new MarginPadding { Bottom = 10 },
Children = new Drawable[] Children = new Drawable[]
{ {
@ -208,7 +169,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
new Dimension(GridSizeMode.Relative, size: 0.4f, minSize: 120), new Dimension(GridSizeMode.Relative, size: 0.4f, minSize: 120),
} }
}, },
null
}, },
}, },
ColumnDimensions = new[] ColumnDimensions = new[]
@ -216,74 +176,22 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400), new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400),
new Dimension(), new Dimension(),
new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600), new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600),
new Dimension(),
} }
} };
}
}, protected override Drawable CreateFooter() => new PlaylistsRoomFooter
}
}
},
new Drawable[]
{ {
new Footer { OnStart = StartPlay } OnStart = StartPlay
} };
},
RowDimensions = new[] protected override RoomSettingsOverlay CreateRoomSettingsOverlay() => new PlaylistsRoomSettingsOverlay
{ {
new Dimension(),
new Dimension(GridSizeMode.AutoSize),
}
},
settingsOverlay = new PlaylistsMatchSettingsOverlay
{
RelativeSizeAxes = Axes.Both,
EditPlaylist = () => EditPlaylist = () =>
{ {
if (this.IsCurrentScreen()) if (this.IsCurrentScreen())
this.Push(new PlaylistsSongSelect()); this.Push(new PlaylistsSongSelect());
}, },
State = { Value = roomId.Value == null ? Visibility.Visible : Visibility.Hidden } };
}
});
if (roomId.Value == null)
{
// A new room is being created.
// The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed.
mainContent.Hide();
settingsOverlay.State.BindValueChanged(visibility =>
{
if (visibility.NewValue == Visibility.Hidden)
mainContent.Show();
}, true);
}
}
[Resolved]
private IAPIProvider api { get; set; }
protected override void LoadComplete()
{
base.LoadComplete();
isIdle.BindValueChanged(_ => updatePollingRate(), true);
roomId.BindValueChanged(id =>
{
if (id.NewValue == null)
settingsOverlay.Show();
else
{
settingsOverlay.Hide();
// Set the first playlist item.
// This is scheduled since updating the room and playlist may happen in an arbitrary order (via Room.CopyFrom()).
Schedule(() => SelectedItem.Value = playlist.FirstOrDefault());
}
}, true);
}
private void updatePollingRate() private void updatePollingRate()
{ {