From a6f823e5bc32bfc0e59fa4a0df82e1c2fff263b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Aug 2025 14:18:13 +0200 Subject: [PATCH 1/3] Show pinned rooms on top of listing --- .../Visual/Multiplayer/TestSceneRoomPanel.cs | 15 ++++++++++++--- osu.Game/Online/Rooms/Room.cs | 9 +++++++++ .../OnlinePlay/Lounge/Components/RoomListing.cs | 3 +-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs index 037c5faae3..ce9ee3a011 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs @@ -81,6 +81,15 @@ namespace osu.Game.Tests.Visual.Multiplayer CurrentPlaylistItem = item1 }), createLoungeRoom(new Room + { + Name = "Pinned room", + Pinned = true, + EndDate = DateTimeOffset.Now.AddDays(1), + Type = MatchType.HeadToHead, + Playlist = [item1], + CurrentPlaylistItem = item1 + }), + createLoungeRoom(new Room { Name = "Private room", Password = "*", @@ -140,13 +149,13 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for panel load", () => panel.ChildrenOfType().Any()); - AddAssert("password icon hidden", () => Precision.AlmostEquals(0, panel.ChildrenOfType().Single().Alpha)); + AddAssert("password icon hidden", () => Precision.AlmostEquals(0, panel.ChildrenOfType().Single().Alpha)); AddStep("set password", () => room.Password = "password"); - AddAssert("password icon visible", () => Precision.AlmostEquals(1, panel.ChildrenOfType().Single().Alpha)); + AddAssert("password icon visible", () => Precision.AlmostEquals(1, panel.ChildrenOfType().Single().Alpha)); AddStep("unset password", () => room.Password = string.Empty); - AddAssert("password icon hidden", () => Precision.AlmostEquals(0, panel.ChildrenOfType().Single().Alpha)); + AddAssert("password icon hidden", () => Precision.AlmostEquals(0, panel.ChildrenOfType().Single().Alpha)); } [Test] diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs index e965f9c187..4200fed0dd 100644 --- a/osu.Game/Online/Rooms/Room.cs +++ b/osu.Game/Online/Rooms/Room.cs @@ -263,6 +263,12 @@ namespace osu.Game.Online.Rooms set => SetField(ref availability, value); } + public bool Pinned + { + get => pinned; + set => SetField(ref pinned, value); + } + [JsonProperty("id")] private long? roomId; @@ -339,6 +345,9 @@ namespace osu.Game.Online.Rooms [JsonConverter(typeof(SnakeCaseStringEnumConverter))] private RoomStatus status; + [JsonProperty("pinned")] + private bool pinned; + // Not yet serialised (not implemented). private RoomAvailability availability; diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomListing.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomListing.cs index 14edd13ec5..b93d26880d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomListing.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomListing.cs @@ -194,8 +194,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components roomFlow.Add(drawableRoom); - // Always show spotlight playlists at the top of the listing. - roomFlow.SetLayoutPosition(drawableRoom, room.Category > RoomCategory.Normal ? float.MinValue : -(room.RoomID ?? 0)); + roomFlow.SetLayoutPosition(drawableRoom, room.Pinned ? float.MinValue : -(room.RoomID ?? 0)); } applyFilterCriteria(Filter.Value); From 20b316d32d3cd4a575e31df5608209a0491588da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Aug 2025 14:18:47 +0200 Subject: [PATCH 2/3] Add indicator for pinned rooms in upper right of room panel --- .../Visual/Multiplayer/TestSceneRoomPanel.cs | 6 +- .../OnlinePlay/Lounge/Components/RoomPanel.cs | 62 ++++++++++++++----- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs index ce9ee3a011..d1b9005e63 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs @@ -149,13 +149,13 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for panel load", () => panel.ChildrenOfType().Any()); - AddAssert("password icon hidden", () => Precision.AlmostEquals(0, panel.ChildrenOfType().Single().Alpha)); + AddAssert("password icon hidden", () => Precision.AlmostEquals(0, panel.ChildrenOfType().First().Alpha)); AddStep("set password", () => room.Password = "password"); - AddAssert("password icon visible", () => Precision.AlmostEquals(1, panel.ChildrenOfType().Single().Alpha)); + AddAssert("password icon visible", () => Precision.AlmostEquals(1, panel.ChildrenOfType().First().Alpha)); AddStep("unset password", () => room.Password = string.Empty); - AddAssert("password icon hidden", () => Precision.AlmostEquals(0, panel.ChildrenOfType().Single().Alpha)); + AddAssert("password icon hidden", () => Precision.AlmostEquals(0, panel.ChildrenOfType().First().Alpha)); } [Test] diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomPanel.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomPanel.cs index 3610995b2c..258c9c3a97 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomPanel.cs @@ -59,7 +59,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private DrawableRoomParticipantsList? drawableRoomParticipantsList; private RoomSpecialCategoryPill? specialCategoryPill; - private PasswordProtectedIcon? passwordIcon; + private CornerIcon? passwordIcon; + private CornerIcon? pinnedIcon; private EndDateInfo? endDateInfo; private RoomNameLine? roomName; private DelayedLoadWrapper wrapper = null!; @@ -88,7 +89,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colours) + private void load(OverlayColourProvider colourProvider, OsuColour colours) { ButtonsContainer = new Container { @@ -104,7 +105,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.Background5, + Colour = colourProvider.Background5, }, CreateBackground().With(d => { @@ -128,7 +129,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.Background5, + Colour = colourProvider.Background5, Width = 0.2f, }, new Box @@ -136,7 +137,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Anchor = Anchor.TopRight, Origin = Anchor.TopRight, RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f)), + Colour = ColourInfo.GradientHorizontal(colourProvider.Background5, colourProvider.Background5.Opacity(0.3f)), Width = 0.8f, }, new GridContainer @@ -254,7 +255,28 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } }, - passwordIcon = new PasswordProtectedIcon { Alpha = 0 } + passwordIcon = new CornerIcon + { + Alpha = 0, + Background = { Colour = colours.Gray8, }, + Icon = + { + Icon = FontAwesome.Solid.Lock, + Colour = colours.Gray3, + Rotation = 45, + }, + }, + pinnedIcon = new CornerIcon + { + Alpha = 0, + Background = { Colour = colours.Orange2 }, + Icon = + { + Icon = FontAwesome.Solid.Thumbtack, + Colour = colours.Gray3, + Rotation = 45, + }, + } }, }, }, 0) @@ -283,6 +305,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components updateRoomCategory(); updateRoomType(); updateRoomHasPassword(); + updateRoomPinned(); }; SelectedItem.BindValueChanged(onSelectedItemChanged, true); @@ -311,6 +334,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components case nameof(Room.HasPassword): updateRoomHasPassword(); break; + + case nameof(Room.Pinned): + updateRoomPinned(); + break; } } @@ -371,6 +398,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components passwordIcon.Alpha = Room.HasPassword ? 1 : 0; } + private void updateRoomPinned() + { + if (pinnedIcon != null) + pinnedIcon.Alpha = Room.Pinned ? 1 : 0; + } + private int numberOfAvatars = 7; public int NumberOfAvatars @@ -534,10 +567,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components } } - public partial class PasswordProtectedIcon : CompositeDrawable + public partial class CornerIcon : CompositeDrawable { - [BackgroundDependencyLoader] - private void load(OsuColour colours) + public SpriteIcon Icon { get; } + public Box Background { get; } + + public CornerIcon() { Anchor = Anchor.TopRight; Origin = Anchor.TopRight; @@ -546,20 +581,19 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components InternalChildren = new Drawable[] { - new Box + Background = new Box { Anchor = Anchor.TopRight, Origin = Anchor.TopCentre, - Colour = colours.Gray5, Rotation = 45, RelativeSizeAxes = Axes.Both, Width = 2, }, - new SpriteIcon + Icon = new SpriteIcon { - Icon = FontAwesome.Solid.Lock, Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, + Origin = Anchor.Centre, + Position = new Vector2(-13, 13), Margin = new MarginPadding(6), Size = new Vector2(14), } From 03e7e2b0d856f3164a872fc9d72bb298418c6535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 22 Aug 2025 11:26:48 +0200 Subject: [PATCH 3/3] Update tests --- .../Visual/Multiplayer/TestSceneRoomListing.cs | 14 +++++++------- .../Visual/Multiplayer/TestSceneRoomPanel.cs | 6 +++--- .../Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomListing.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomListing.cs index 58473f5fa2..7f6fb97e0c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomListing.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomListing.cs @@ -52,25 +52,25 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestBasicListChanges() { - AddStep("add rooms", () => rooms.AddRange(GenerateRooms(5, withSpotlightRooms: true))); + AddStep("add rooms", () => rooms.AddRange(GenerateRooms(5, withPinnedRooms: true))); AddAssert("has 5 rooms", () => container.DrawableRooms.Count == 5); - AddAssert("all spotlights at top", () => container.DrawableRooms - .SkipWhile(r => r.Room.Category == RoomCategory.Spotlight) - .All(r => r.Room.Category == RoomCategory.Normal)); + AddAssert("all pinned at top", () => container.DrawableRooms + .SkipWhile(r => r.Room.Pinned) + .All(r => !r.Room.Pinned)); AddStep("remove first room", () => rooms.RemoveAt(0)); AddAssert("has 4 rooms", () => container.DrawableRooms.Count == 4); AddAssert("first room removed", () => container.DrawableRooms.All(r => r.Room.RoomID != 0)); AddStep("select first room", () => container.DrawableRooms.First().TriggerClick()); - AddAssert("first spotlight selected", () => checkRoomSelected(rooms.First(r => r.Category == RoomCategory.Spotlight))); + AddAssert("first pinned room selected", () => checkRoomSelected(rooms.First(r => r.Pinned))); AddStep("remove last room", () => rooms.RemoveAt(rooms.Count - 1)); - AddAssert("first spotlight still selected", () => checkRoomSelected(rooms.First(r => r.Category == RoomCategory.Spotlight))); + AddAssert("first pinned room selected", () => checkRoomSelected(rooms.First(r => r.Pinned))); - AddStep("remove spotlight room", () => rooms.RemoveAll(r => r.Category == RoomCategory.Spotlight)); + AddStep("remove pinned rooms", () => rooms.RemoveAll(r => r.Pinned)); AddAssert("selection vacated", () => checkRoomSelected(null)); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs index d1b9005e63..58eb0f1ea1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneRoomPanel.cs @@ -130,9 +130,9 @@ namespace osu.Game.Tests.Visual.Multiplayer }; }); - AddUntilStep("wait for panel load", () => rooms.Count == 7); - AddUntilStep("correct status text", () => rooms.ChildrenOfType().Count(s => s.Text.ToString().StartsWith("Currently playing", StringComparison.Ordinal)) == 2); - AddUntilStep("correct status text", () => rooms.ChildrenOfType().Count(s => s.Text.ToString().StartsWith("Ready to play", StringComparison.Ordinal)) == 5); + AddUntilStep("wait for panel load", () => rooms.Count, () => Is.EqualTo(8)); + AddUntilStep("\"currently playing\" room count correct", () => rooms.ChildrenOfType().Count(s => s.Text.ToString().StartsWith("Currently playing", StringComparison.Ordinal)), () => Is.EqualTo(3)); + AddUntilStep("\"ready to play\" room count correct", () => rooms.ChildrenOfType().Count(s => s.Text.ToString().StartsWith("Ready to play", StringComparison.Ordinal)), () => Is.EqualTo(4)); } [Test] diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs index 914d187864..c687815270 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// protected virtual OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new OnlinePlayTestSceneDependencies(); - protected Room[] GenerateRooms(int count, RulesetInfo? ruleset = null, bool withPassword = false, bool withSpotlightRooms = false) + protected Room[] GenerateRooms(int count, RulesetInfo? ruleset = null, bool withPassword = false, bool withPinnedRooms = false) { Room[] rooms = new Room[count]; @@ -110,10 +110,10 @@ namespace osu.Game.Tests.Visual.OnlinePlay Name = $@"Room {currentRoomId}", Host = new APIUser { Username = @"Host" }, Duration = TimeSpan.FromSeconds(10), - Category = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal, Password = withPassword ? @"password" : null, PlaylistItemStats = new Room.RoomPlaylistItemStats { RulesetIDs = [ruleset.OnlineID] }, - Playlist = [new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() }) { RulesetID = ruleset.OnlineID }] + Playlist = [new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() }) { RulesetID = ruleset.OnlineID }], + Pinned = withPinnedRooms && i % 2 == 0, }; }