Merge branch 'master' into multiplayer-team-vs-results

This commit is contained in:
Bartłomiej Dach 2021-08-13 21:30:44 +02:00
commit 8f3fedade3
No known key found for this signature in database
GPG Key ID: BCECCD4FA41F6497
25 changed files with 371 additions and 428 deletions

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
{
drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield.HitObjectContainer, drawableRuleset.Beatmap));
drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield, drawableRuleset.Beatmap));
}
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
@ -128,8 +129,21 @@ namespace osu.Game.Rulesets.Osu.Mods
protected override void Update()
{
float start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X;
float end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X;
float start, end;
if (Precision.AlmostEquals(restrictTo.Rotation, 0))
{
start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X;
end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X;
}
else
{
float center = restrictTo.ToSpaceOfOtherDrawable(restrictTo.OriginPosition, Parent).X;
float halfDiagonal = (restrictTo.DrawSize / 2).LengthFast;
start = center - halfDiagonal;
end = center + halfDiagonal;
}
float rawWidth = end - start;

View File

@ -0,0 +1,42 @@
// 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.Framework.Testing;
using osu.Game.Overlays;
using osu.Game.Tests.Visual.Navigation;
namespace osu.Game.Tests.Visual.Menus
{
public class TestSceneSideOverlays : OsuGameTestScene
{
[SetUpSteps]
public override void SetUpSteps()
{
base.SetUpSteps();
AddAssert("no screen offset applied", () => Game.ScreenOffsetContainer.X == 0f);
AddUntilStep("wait for overlays", () => Game.Settings.IsLoaded && Game.Notifications.IsLoaded);
}
[Test]
public void TestScreenOffsettingOnSettingsOverlay()
{
AddStep("open settings", () => Game.Settings.Show());
AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO);
AddStep("hide settings", () => Game.Settings.Hide());
AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f);
}
[Test]
public void TestScreenOffsettingOnNotificationOverlay()
{
AddStep("open notifications", () => Game.Notifications.Show());
AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == -NotificationOverlay.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO);
AddStep("hide notifications", () => Game.Notifications.Hide());
AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f);
}
}
}

View File

@ -87,6 +87,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestEmpty()
{
// used to test the flow of multiplayer from visual tests.
AddStep("empty step", () => { });
}
[Test]
@ -408,8 +409,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden);
testLeave("lounge tab item", () => this.ChildrenOfType<BreadcrumbControl<IScreen>.BreadcrumbTabItem>().First().TriggerClick());
testLeave("back button", () => multiplayerScreen.OnBackButton());
// mimics home button and OS window close

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
using osu.Framework.Screens;
@ -95,6 +96,8 @@ namespace osu.Game.Tests.Visual.Navigation
public class TestOsuGame : OsuGame
{
public new const float SIDE_OVERLAY_OFFSET_RATIO = OsuGame.SIDE_OVERLAY_OFFSET_RATIO;
public new ScreenStack ScreenStack => base.ScreenStack;
public new BackButton BackButton => base.BackButton;
@ -103,7 +106,11 @@ namespace osu.Game.Tests.Visual.Navigation
public new ScoreManager ScoreManager => base.ScoreManager;
public new SettingsPanel Settings => base.Settings;
public new Container ScreenOffsetContainer => base.ScreenOffsetContainer;
public new SettingsOverlay Settings => base.Settings;
public new NotificationOverlay Notifications => base.Notifications;
public new MusicController MusicController => base.MusicController;

View File

@ -1,23 +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.Graphics;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
namespace osu.Game.Tests.Visual.Playlists
{
public class TestScenePlaylistsFilterControl : OsuTestScene
{
public TestScenePlaylistsFilterControl()
{
Child = new PlaylistsFilterControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Width = 0.7f,
Height = 80,
};
}
}
}

View File

@ -62,6 +62,24 @@ namespace osu.Game.Tests.Visual.Playlists
AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms[^1]));
}
[Test]
public void TestEnteringRoomTakesLeaseOnSelection()
{
AddStep("add rooms", () => RoomManager.AddRooms(1));
AddAssert("selected room is not disabled", () => !OnlinePlayDependencies.SelectedRoom.Disabled);
AddStep("select room", () => roomsContainer.Rooms[0].TriggerClick());
AddAssert("selected room is non-null", () => OnlinePlayDependencies.SelectedRoom.Value != null);
AddStep("enter room", () => roomsContainer.Rooms[0].TriggerClick());
AddUntilStep("wait for match load", () => Stack.CurrentScreen is PlaylistsRoomSubScreen);
AddAssert("selected room is non-null", () => OnlinePlayDependencies.SelectedRoom.Value != null);
AddAssert("selected room is disabled", () => OnlinePlayDependencies.SelectedRoom.Disabled);
}
private bool checkRoomVisible(DrawableRoom room) =>
loungeScreen.ChildrenOfType<OsuScrollContainer>().First().ScreenSpaceDrawQuad
.Contains(room.ScreenSpaceDrawQuad.Centre);

View File

@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Settings
new TabletSettings(tabletHandler)
{
RelativeSizeAxes = Axes.None,
Width = SettingsPanel.WIDTH,
Width = SettingsPanel.PANEL_WIDTH,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
}

View File

@ -64,6 +64,11 @@ namespace osu.Game
/// </summary>
public class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>
{
/// <summary>
/// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications).
/// </summary>
protected const float SIDE_OVERLAY_OFFSET_RATIO = 0.05f;
public Toolbar Toolbar;
private ChatOverlay chatOverlay;
@ -71,7 +76,7 @@ namespace osu.Game
private ChannelManager channelManager;
[NotNull]
private readonly NotificationOverlay notifications = new NotificationOverlay();
protected readonly NotificationOverlay Notifications = new NotificationOverlay();
private BeatmapListingOverlay beatmapListing;
@ -97,7 +102,7 @@ namespace osu.Game
private ScalingContainer screenContainer;
private Container screenOffsetContainer;
protected Container ScreenOffsetContainer { get; private set; }
[Resolved]
private FrameworkConfigManager frameworkConfig { get; set; }
@ -312,7 +317,7 @@ namespace osu.Game
case LinkAction.OpenEditorTimestamp:
case LinkAction.JoinMultiplayerMatch:
case LinkAction.Spectate:
waitForReady(() => notifications, _ => notifications.Post(new SimpleNotification
waitForReady(() => Notifications, _ => Notifications.Post(new SimpleNotification
{
Text = @"This link type is not yet supported!",
Icon = FontAwesome.Solid.LifeRing,
@ -611,12 +616,12 @@ namespace osu.Game
MenuCursorContainer.CanShowCursor = menuScreen?.CursorVisible ?? false;
// todo: all archive managers should be able to be looped here.
SkinManager.PostNotification = n => notifications.Post(n);
SkinManager.PostNotification = n => Notifications.Post(n);
BeatmapManager.PostNotification = n => notifications.Post(n);
BeatmapManager.PostNotification = n => Notifications.Post(n);
BeatmapManager.PresentImport = items => PresentBeatmap(items.First());
ScoreManager.PostNotification = n => notifications.Post(n);
ScoreManager.PostNotification = n => Notifications.Post(n);
ScoreManager.PresentImport = items => PresentScore(items.First());
// make config aware of how to lookup skins for on-screen display purposes.
@ -655,7 +660,7 @@ namespace osu.Game
ActionRequested = action => volume.Adjust(action),
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
},
screenOffsetContainer = new Container
ScreenOffsetContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
@ -724,7 +729,7 @@ namespace osu.Game
loadComponentSingleFile(onScreenDisplay, Add, true);
loadComponentSingleFile(notifications.With(d =>
loadComponentSingleFile(Notifications.With(d =>
{
d.GetToolbarHeight = () => ToolbarOffset;
d.Anchor = Anchor.TopRight;
@ -733,7 +738,7 @@ namespace osu.Game
loadComponentSingleFile(new CollectionManager(Storage)
{
PostNotification = n => notifications.Post(n),
PostNotification = n => Notifications.Post(n),
}, Add, true);
loadComponentSingleFile(stableImportManager, Add);
@ -785,7 +790,7 @@ namespace osu.Game
Add(new MusicKeyBindingHandler());
// side overlays which cancel each other.
var singleDisplaySideOverlays = new OverlayContainer[] { Settings, notifications };
var singleDisplaySideOverlays = new OverlayContainer[] { Settings, Notifications };
foreach (var overlay in singleDisplaySideOverlays)
{
@ -828,21 +833,6 @@ namespace osu.Game
{
if (mode.NewValue != OverlayActivation.All) CloseAllOverlays();
};
void updateScreenOffset()
{
float offset = 0;
if (Settings.State.Value == Visibility.Visible)
offset += Toolbar.HEIGHT / 2;
if (notifications.State.Value == Visibility.Visible)
offset -= Toolbar.HEIGHT / 2;
screenOffsetContainer.MoveToX(offset, SettingsPanel.TRANSITION_LENGTH, Easing.OutQuint);
}
Settings.State.ValueChanged += _ => updateScreenOffset();
notifications.State.ValueChanged += _ => updateScreenOffset();
}
private void showOverlayAboveOthers(OverlayContainer overlay, OverlayContainer[] otherOverlays)
@ -874,7 +864,7 @@ namespace osu.Game
if (recentLogCount < short_term_display_limit)
{
Schedule(() => notifications.Post(new SimpleErrorNotification
Schedule(() => Notifications.Post(new SimpleErrorNotification
{
Icon = entry.Level == LogLevel.Important ? FontAwesome.Solid.ExclamationCircle : FontAwesome.Solid.Bomb,
Text = entry.Message.Truncate(256) + (entry.Exception != null && IsDeployedBuild ? "\n\nThis error has been automatically reported to the devs." : string.Empty),
@ -882,7 +872,7 @@ namespace osu.Game
}
else if (recentLogCount == short_term_display_limit)
{
Schedule(() => notifications.Post(new SimpleNotification
Schedule(() => Notifications.Post(new SimpleNotification
{
Icon = FontAwesome.Solid.EllipsisH,
Text = "Subsequent messages have been logged. Click to view log files.",
@ -1023,9 +1013,18 @@ namespace osu.Game
{
base.UpdateAfterChildren();
screenOffsetContainer.Padding = new MarginPadding { Top = ToolbarOffset };
ScreenOffsetContainer.Padding = new MarginPadding { Top = ToolbarOffset };
overlayContent.Padding = new MarginPadding { Top = ToolbarOffset };
var horizontalOffset = 0f;
if (Settings.IsLoaded && Settings.IsPresent)
horizontalOffset += ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X * SIDE_OVERLAY_OFFSET_RATIO;
if (Notifications.IsLoaded && Notifications.IsPresent)
horizontalOffset += (ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - DrawWidth) * SIDE_OVERLAY_OFFSET_RATIO;
ScreenOffsetContainer.X = horizontalOffset;
MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
}

View File

@ -24,7 +24,7 @@ namespace osu.Game.Overlays
public LocalisableString Title => NotificationsStrings.HeaderTitle;
public LocalisableString Description => NotificationsStrings.HeaderDescription;
private const float width = 320;
public const float WIDTH = 320;
public const float TRANSITION_LENGTH = 600;
@ -38,7 +38,8 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader]
private void load()
{
Width = width;
X = WIDTH;
Width = WIDTH;
RelativeSizeAxes = Axes.Y;
Children = new Drawable[]
@ -152,7 +153,7 @@ namespace osu.Game.Overlays
markAllRead();
this.MoveToX(width, TRANSITION_LENGTH, Easing.OutQuint);
this.MoveToX(WIDTH, TRANSITION_LENGTH, Easing.OutQuint);
this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint);
}

View File

@ -9,7 +9,6 @@ using osu.Game.Overlays.Settings.Sections;
using osu.Game.Overlays.Settings.Sections.Input;
using osuTK.Graphics;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Localisation;
using osu.Game.Localisation;
@ -38,6 +37,8 @@ namespace osu.Game.Overlays
private readonly List<SettingsSubPanel> subPanels = new List<SettingsSubPanel>();
private SettingsSubPanel lastOpenedSubPanel;
protected override Drawable CreateHeader() => new SettingsHeader(Title, Description);
protected override Drawable CreateFooter() => new SettingsFooter();
@ -46,21 +47,21 @@ namespace osu.Game.Overlays
{
}
public override bool AcceptsFocus => subPanels.All(s => s.State.Value != Visibility.Visible);
public override bool AcceptsFocus => lastOpenedSubPanel == null || lastOpenedSubPanel.State.Value == Visibility.Hidden;
private T createSubPanel<T>(T subPanel)
where T : SettingsSubPanel
{
subPanel.Depth = 1;
subPanel.Anchor = Anchor.TopRight;
subPanel.State.ValueChanged += subPanelStateChanged;
subPanel.State.ValueChanged += e => subPanelStateChanged(subPanel, e);
subPanels.Add(subPanel);
return subPanel;
}
private void subPanelStateChanged(ValueChangedEvent<Visibility> state)
private void subPanelStateChanged(SettingsSubPanel panel, ValueChangedEvent<Visibility> state)
{
switch (state.NewValue)
{
@ -68,7 +69,9 @@ namespace osu.Game.Overlays
Sidebar?.FadeColour(Color4.DarkGray, 300, Easing.OutQuint);
SectionsContainer.FadeOut(300, Easing.OutQuint);
ContentContainer.MoveToX(-WIDTH, 500, Easing.OutQuint);
ContentContainer.MoveToX(-PANEL_WIDTH, 500, Easing.OutQuint);
lastOpenedSubPanel = panel;
break;
case Visibility.Hidden:
@ -80,7 +83,7 @@ namespace osu.Game.Overlays
}
}
protected override float ExpandedPosition => subPanels.Any(s => s.State.Value == Visibility.Visible) ? -WIDTH : base.ExpandedPosition;
protected override float ExpandedPosition => lastOpenedSubPanel?.State.Value == Visibility.Visible ? -PANEL_WIDTH : base.ExpandedPosition;
[BackgroundDependencyLoader]
private void load()

View File

@ -28,7 +28,15 @@ namespace osu.Game.Overlays
private const float sidebar_width = Sidebar.DEFAULT_WIDTH;
public const float WIDTH = 400;
/// <summary>
/// The width of the settings panel content, excluding the sidebar.
/// </summary>
public const float PANEL_WIDTH = 400;
/// <summary>
/// The full width of the settings panel, including the sidebar.
/// </summary>
public const float WIDTH = sidebar_width + PANEL_WIDTH;
protected Container<Drawable> ContentContainer;
@ -64,7 +72,8 @@ namespace osu.Game.Overlays
{
InternalChild = ContentContainer = new NonMaskedContent
{
Width = WIDTH,
X = -WIDTH + ExpandedPosition,
Width = PANEL_WIDTH,
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{

View File

@ -2,19 +2,15 @@
// See the LICENCE file in the repository root for full licence text.
using Humanizer;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Screens;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay
{
@ -22,52 +18,30 @@ namespace osu.Game.Screens.OnlinePlay
{
public const float HEIGHT = 80;
private readonly ScreenStack stack;
private readonly MultiHeaderTitle title;
public Header(string mainTitle, ScreenStack stack)
{
this.stack = stack;
RelativeSizeAxes = Axes.X;
Height = HEIGHT;
Padding = new MarginPadding { Left = WaveOverlayContainer.WIDTH_PADDING };
HeaderBreadcrumbControl breadcrumbs;
MultiHeaderTitle title;
Children = new Drawable[]
Child = title = new MultiHeaderTitle(mainTitle)
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex(@"#1f1921"),
},
new Container
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = WaveOverlayContainer.WIDTH_PADDING + OsuScreen.HORIZONTAL_OVERFLOW_PADDING },
Children = new Drawable[]
{
title = new MultiHeaderTitle(mainTitle)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomLeft,
},
breadcrumbs = new HeaderBreadcrumbControl(stack)
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft
}
},
},
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
};
breadcrumbs.Current.ValueChanged += screen =>
{
if (screen.NewValue is IOnlinePlaySubScreen onlineSubScreen)
title.Screen = onlineSubScreen;
};
breadcrumbs.Current.TriggerChange();
// unnecessary to unbind these as this header has the same lifetime as the screen stack we are attaching to.
stack.ScreenPushed += (_, __) => updateSubScreenTitle();
stack.ScreenExited += (_, __) => updateSubScreenTitle();
}
private void updateSubScreenTitle() => title.Screen = stack.CurrentScreen as IOnlinePlaySubScreen;
private class MultiHeaderTitle : CompositeDrawable
{
private const float spacing = 6;
@ -75,9 +49,10 @@ namespace osu.Game.Screens.OnlinePlay
private readonly OsuSpriteText dot;
private readonly OsuSpriteText pageTitle;
[CanBeNull]
public IOnlinePlaySubScreen Screen
{
set => pageTitle.Text = value.ShortTitle.Titleize();
set => pageTitle.Text = value?.ShortTitle.Titleize() ?? string.Empty;
}
public MultiHeaderTitle(string mainTitle)
@ -125,35 +100,5 @@ namespace osu.Game.Screens.OnlinePlay
pageTitle.Colour = dot.Colour = colours.Yellow;
}
}
private class HeaderBreadcrumbControl : ScreenBreadcrumbControl
{
public HeaderBreadcrumbControl(ScreenStack stack)
: base(stack)
{
RelativeSizeAxes = Axes.X;
StripColour = Color4.Transparent;
}
protected override void LoadComplete()
{
base.LoadComplete();
AccentColour = Color4Extensions.FromHex("#e35c99");
}
protected override TabItem<IScreen> CreateTabItem(IScreen value) => new HeaderBreadcrumbTabItem(value)
{
AccentColour = AccentColour
};
private class HeaderBreadcrumbTabItem : BreadcrumbTabItem
{
public HeaderBreadcrumbTabItem(IScreen value)
: base(value)
{
Bar.Colour = Color4.Transparent;
}
}
}
}
}

View File

@ -1,125 +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.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
public abstract class FilterControl : CompositeDrawable
{
protected readonly FillFlowContainer Filters;
[Resolved(CanBeNull = true)]
private Bindable<FilterCriteria> filter { get; set; }
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; }
private readonly SearchTextBox search;
private readonly Dropdown<RoomStatusFilter> statusDropdown;
protected FilterControl()
{
RelativeSizeAxes = Axes.X;
Height = 70;
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
Children = new Drawable[]
{
search = new FilterSearchTextBox
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.X,
Width = 0.6f,
},
Filters = new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10),
Child = statusDropdown = new SlimEnumDropdown<RoomStatusFilter>
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.None,
Width = 160,
}
},
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
filter ??= new Bindable<FilterCriteria>();
}
protected override void LoadComplete()
{
base.LoadComplete();
search.Current.BindValueChanged(_ => updateFilterDebounced());
ruleset.BindValueChanged(_ => UpdateFilter());
statusDropdown.Current.BindValueChanged(_ => UpdateFilter(), true);
}
private ScheduledDelegate scheduledFilterUpdate;
private void updateFilterDebounced()
{
scheduledFilterUpdate?.Cancel();
scheduledFilterUpdate = Scheduler.AddDelayed(UpdateFilter, 200);
}
protected void UpdateFilter() => Scheduler.AddOnce(updateFilter);
private void updateFilter()
{
scheduledFilterUpdate?.Cancel();
var criteria = CreateCriteria();
criteria.SearchString = search.Current.Value;
criteria.Status = statusDropdown.Current.Value;
criteria.Ruleset = ruleset.Value;
filter.Value = criteria;
}
protected virtual FilterCriteria CreateCriteria() => new FilterCriteria();
public bool HoldFocus
{
get => search.HoldFocus;
set => search.HoldFocus = value;
}
public void TakeFocus() => search.TakeFocus();
private class FilterSearchTextBox : SearchTextBox
{
[BackgroundDependencyLoader]
private void load()
{
BackgroundUnfocused = OsuColour.Gray(0.06f);
BackgroundFocused = OsuColour.Gray(0.12f);
}
}
}
}

View File

@ -1,57 +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.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
public class PlaylistsFilterControl : FilterControl
{
private readonly Dropdown<PlaylistsCategory> categoryDropdown;
public PlaylistsFilterControl()
{
Filters.Add(categoryDropdown = new SlimEnumDropdown<PlaylistsCategory>
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.None,
Width = 160,
});
}
protected override void LoadComplete()
{
base.LoadComplete();
categoryDropdown.Current.BindValueChanged(_ => UpdateFilter());
}
protected override FilterCriteria CreateCriteria()
{
var criteria = base.CreateCriteria();
switch (categoryDropdown.Current.Value)
{
case PlaylistsCategory.Normal:
criteria.Category = "normal";
break;
case PlaylistsCategory.Spotlight:
criteria.Category = "spotlight";
break;
}
return criteria;
}
private enum PlaylistsCategory
{
Any,
Normal,
Spotlight
}
}
}

View File

@ -140,7 +140,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
roomFlow.Remove(toRemove);
selectedRoom.Value = null;
// selection may have a lease due to being in a sub screen.
if (!selectedRoom.Disabled)
selectedRoom.Value = null;
}
}
@ -152,7 +154,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
protected override bool OnClick(ClickEvent e)
{
selectedRoom.Value = null;
if (!selectedRoom.Disabled)
selectedRoom.Value = null;
return base.OnClick(e);
}
@ -214,6 +217,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
private void selectNext(int direction)
{
if (selectedRoom.Disabled)
return;
var visibleRooms = Rooms.AsEnumerable().Where(r => r.IsPresent);
Room room;

View File

@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
@ -9,18 +11,20 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay.Lounge
{
@ -41,7 +45,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
private readonly IBindable<bool> initialRoomsReceived = new Bindable<bool>();
private readonly IBindable<bool> operationInProgress = new Bindable<bool>();
private FilterControl filter;
private LoadingLayer loadingLayer;
[Resolved]
@ -53,31 +56,37 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
[Resolved(CanBeNull = true)]
private OngoingOperationTracker ongoingOperationTracker { get; set; }
[Resolved(CanBeNull = true)]
private Bindable<FilterCriteria> filter { get; set; }
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; }
[CanBeNull]
private IDisposable joiningRoomOperation { get; set; }
private RoomsContainer roomsContainer;
private SearchTextBox searchTextBox;
private Dropdown<RoomStatusFilter> statusDropdown;
[CanBeNull]
private LeasedBindable<Room> selectionLease;
[BackgroundDependencyLoader]
private void load()
{
filter ??= new Bindable<FilterCriteria>(new FilterCriteria());
OsuScrollContainer scrollContainer;
InternalChildren = new Drawable[]
InternalChildren = new[]
{
new Box
{
RelativeSizeAxes = Axes.X,
Height = 100,
Colour = Color4.Black,
Alpha = 0.5f,
},
loadingLayer = new LoadingLayer(true),
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Top = 20,
Left = WaveOverlayContainer.WIDTH_PADDING,
Right = WaveOverlayContainer.WIDTH_PADDING,
},
@ -86,26 +95,50 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
RelativeSizeAxes = Axes.Both,
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.Absolute, Header.HEIGHT),
new Dimension(GridSizeMode.Absolute, 25),
new Dimension(GridSizeMode.Absolute, 20)
},
Content = new[]
{
new Drawable[]
{
searchTextBox = new LoungeSearchTextBox
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.X,
Width = 0.6f,
},
},
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Height = 70,
Depth = -1,
RelativeSizeAxes = Axes.Both,
Depth = float.MinValue, // Contained filters should appear over the top of rooms.
Children = new Drawable[]
{
filter = CreateFilterControl(),
Buttons.WithChild(CreateNewRoomButton().With(d =>
{
d.Size = new Vector2(150, 25);
d.Anchor = Anchor.BottomLeft;
d.Origin = Anchor.BottomLeft;
d.Size = new Vector2(150, 37.5f);
d.Action = () => Open();
}))
})),
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10),
ChildrenEnumerable = CreateFilterControls().Select(f => f.With(d =>
{
d.Anchor = Anchor.TopRight;
d.Origin = Anchor.TopRight;
}))
}
}
}
},
@ -123,13 +156,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
ScrollbarOverlapsContent = false,
Child = roomsContainer = new RoomsContainer()
},
loadingLayer = new LoadingLayer(true),
}
},
}
}
},
}
},
};
// scroll selected room into view on selection.
@ -145,6 +177,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
{
base.LoadComplete();
searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced());
ruleset.BindValueChanged(_ => UpdateFilter());
initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived);
initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer());
@ -153,13 +188,50 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
operationInProgress.BindTo(ongoingOperationTracker.InProgress);
operationInProgress.BindValueChanged(_ => updateLoadingLayer(), true);
}
updateFilter();
}
protected override void OnFocus(FocusEvent e)
#region Filtering
protected void UpdateFilter() => Scheduler.AddOnce(updateFilter);
private ScheduledDelegate scheduledFilterUpdate;
private void updateFilterDebounced()
{
filter.TakeFocus();
scheduledFilterUpdate?.Cancel();
scheduledFilterUpdate = Scheduler.AddDelayed(UpdateFilter, 200);
}
private void updateFilter()
{
scheduledFilterUpdate?.Cancel();
filter.Value = CreateFilterCriteria();
}
protected virtual FilterCriteria CreateFilterCriteria() => new FilterCriteria
{
SearchString = searchTextBox.Current.Value,
Ruleset = ruleset.Value,
Status = statusDropdown.Current.Value
};
protected virtual IEnumerable<Drawable> CreateFilterControls()
{
statusDropdown = new SlimEnumDropdown<RoomStatusFilter>
{
RelativeSizeAxes = Axes.None,
Width = 160,
};
statusDropdown.Current.BindValueChanged(_ => UpdateFilter());
yield return statusDropdown;
}
#endregion
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
@ -171,6 +243,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
{
base.OnResuming(last);
Debug.Assert(selectionLease != null);
selectionLease.Return();
selectionLease = null;
if (selectedRoom.Value?.RoomID.Value == null)
selectedRoom.Value = new Room();
@ -191,14 +268,19 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
base.OnSuspending(next);
}
protected override void OnFocus(FocusEvent e)
{
searchTextBox.TakeFocus();
}
private void onReturning()
{
filter.HoldFocus = true;
searchTextBox.HoldFocus = true;
}
private void onLeaving()
{
filter.HoldFocus = false;
searchTextBox.HoldFocus = false;
// ensure any password prompt is dismissed.
this.HidePopover();
@ -238,13 +320,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
protected virtual void OpenNewRoom(Room room)
{
selectedRoom.Value = room;
selectionLease = selectedRoom.BeginLease(false);
Debug.Assert(selectionLease != null);
selectionLease.Value = room;
this.Push(CreateRoomSubScreen(room));
}
protected abstract FilterControl CreateFilterControl();
protected abstract OsuButton CreateNewRoomButton();
/// <summary>
@ -262,5 +344,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
else
loadingLayer.Hide();
}
private class LoungeSearchTextBox : SearchTextBox
{
[BackgroundDependencyLoader]
private void load()
{
BackgroundUnfocused = OsuColour.Gray(0.06f);
BackgroundFocused = OsuColour.Gray(0.12f);
}
}
}
}

View File

@ -8,8 +8,10 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
using osu.Game.Audio;
using osu.Game.Beatmaps;
@ -62,8 +64,15 @@ namespace osu.Game.Screens.OnlinePlay.Match
protected RoomSubScreen()
{
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
{
SelectedItem = { BindTarget = SelectedItem }

View File

@ -1,17 +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.Game.Screens.OnlinePlay.Lounge.Components;
namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
public class MultiplayerFilterControl : FilterControl
{
protected override FilterCriteria CreateCriteria()
{
var criteria = base.CreateCriteria();
criteria.Category = "realtime";
return criteria;
}
}
}

View File

@ -21,7 +21,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
[Resolved]
private MultiplayerClient client { get; set; }
protected override FilterControl CreateFilterControl() => new MultiplayerFilterControl();
protected override FilterCriteria CreateFilterCriteria()
{
var criteria = base.CreateFilterCriteria();
criteria.Category = @"realtime";
return criteria;
}
protected override OsuButton CreateNewRoomButton() => new CreateMultiplayerMatchButton();

View File

@ -21,8 +21,9 @@ using osu.Game.Screens.Menu;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.OnlinePlay
{
@ -71,9 +72,6 @@ namespace osu.Game.Screens.OnlinePlay
[Resolved(CanBeNull = true)]
private OsuLogo logo { get; set; }
private Drawable header;
private Drawable headerBackground;
protected OnlinePlayScreen()
{
Anchor = Anchor.Centre;
@ -104,42 +102,26 @@ namespace osu.Game.Screens.OnlinePlay
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = Header.HEIGHT },
Children = new[]
Children = new Drawable[]
{
header = new Container
new BufferedContainer
{
RelativeSizeAxes = Axes.X,
Height = 400,
Children = new[]
RelativeSizeAxes = Axes.Both,
BlurSigma = new Vector2(10),
Child = new BeatmapBackgroundSprite
{
headerBackground = new Container
{
RelativeSizeAxes = Axes.Both,
Width = 1.25f,
Masking = true,
Children = new Drawable[]
{
new HeaderBackgroundSprite
{
RelativeSizeAxes = Axes.X,
Height = 400 // Keep a static height so the header doesn't change as it's resized between subscreens
},
}
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Bottom = -1 }, // 1px padding to avoid a 1px gap due to masking
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(backgroundColour.Opacity(0.5f), backgroundColour)
},
}
RelativeSizeAxes = Axes.Both
}
},
screenStack = new OnlinePlaySubScreenStack { RelativeSizeAxes = Axes.Both }
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.9f), Color4.Black.Opacity(0.6f))
},
screenStack = new OnlinePlaySubScreenStack
{
RelativeSizeAxes = Axes.Both
}
}
},
new Header(ScreenTitle, screenStack),
@ -292,19 +274,6 @@ namespace osu.Game.Screens.OnlinePlay
private void subScreenChanged(IScreen lastScreen, IScreen newScreen)
{
switch (newScreen)
{
case LoungeSubScreen _:
header.Delay(OnlinePlaySubScreen.RESUME_TRANSITION_DELAY).ResizeHeightTo(400, OnlinePlaySubScreen.APPEAR_DURATION, Easing.OutQuint);
headerBackground.MoveToX(0, OnlinePlaySubScreen.X_MOVE_DURATION, Easing.OutQuint);
break;
case RoomSubScreen _:
header.ResizeHeightTo(135, OnlinePlaySubScreen.APPEAR_DURATION, Easing.OutQuint);
headerBackground.MoveToX(-OnlinePlaySubScreen.X_SHIFT, OnlinePlaySubScreen.X_MOVE_DURATION, Easing.OutQuint);
break;
}
if (lastScreen is IOsuScreen lastOsuScreen)
Activity.UnbindFrom(lastOsuScreen.Activity);
@ -335,13 +304,13 @@ namespace osu.Game.Screens.OnlinePlay
}
}
private class HeaderBackgroundSprite : OnlinePlayBackgroundSprite
private class BeatmapBackgroundSprite : OnlinePlayBackgroundSprite
{
protected override UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new BackgroundSprite { RelativeSizeAxes = Axes.Both };
private class BackgroundSprite : UpdateableBeatmapBackgroundSprite
{
protected override double TransformDuration => 200;
protected override double LoadDelay => 200;
}
}

View File

@ -59,6 +59,8 @@ namespace osu.Game.Screens.OnlinePlay
[BackgroundDependencyLoader]
private void load()
{
LeftArea.Padding = new MarginPadding { Top = Header.HEIGHT };
initialBeatmap = Beatmap.Value;
initialRuleset = Ruleset.Value;
initialMods = Mods.Value.ToList();

View File

@ -1,7 +1,11 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
@ -16,7 +20,38 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
[Resolved]
private IAPIProvider api { get; set; }
protected override FilterControl CreateFilterControl() => new PlaylistsFilterControl();
private Dropdown<PlaylistsCategory> categoryDropdown;
protected override IEnumerable<Drawable> CreateFilterControls()
{
categoryDropdown = new SlimEnumDropdown<PlaylistsCategory>
{
RelativeSizeAxes = Axes.None,
Width = 160,
};
categoryDropdown.Current.BindValueChanged(_ => UpdateFilter());
return base.CreateFilterControls().Append(categoryDropdown);
}
protected override FilterCriteria CreateFilterCriteria()
{
var criteria = base.CreateFilterCriteria();
switch (categoryDropdown.Current.Value)
{
case PlaylistsCategory.Normal:
criteria.Category = @"normal";
break;
case PlaylistsCategory.Spotlight:
criteria.Category = @"spotlight";
break;
}
return criteria;
}
protected override OsuButton CreateNewRoomButton() => new CreatePlaylistsRoomButton();
@ -30,5 +65,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
}
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room);
private enum PlaylistsCategory
{
Any,
Normal,
Spotlight
}
}
}

View File

@ -35,6 +35,7 @@ namespace osu.Game.Screens.Select
{
public class BeatmapInfoWedge : VisibilityContainer
{
public const float BORDER_THICKNESS = 2.5f;
private const float shear_width = 36.75f;
private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0);
@ -59,7 +60,7 @@ namespace osu.Game.Screens.Select
Shear = wedged_container_shear;
Masking = true;
BorderColour = new Color4(221, 255, 255, 255);
BorderThickness = 2.5f;
BorderThickness = BORDER_THICKNESS;
Alpha = 0;
EdgeEffect = new EdgeEffectParameters
{

View File

@ -56,7 +56,7 @@ namespace osu.Game.Screens.Select
set
{
searchText = value;
SearchTerms = searchText.Split(new[] { ',', ' ', '!' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
SearchTerms = searchText.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToArray();
SearchNumber = null;

View File

@ -79,6 +79,8 @@ namespace osu.Game.Screens.Select
protected BeatmapCarousel Carousel { get; private set; }
protected Container LeftArea { get; private set; }
private BeatmapInfoWedge beatmapInfoWedge;
private DialogOverlay dialogOverlay;
@ -186,12 +188,12 @@ namespace osu.Game.Screens.Select
{
new Drawable[]
{
new Container
LeftArea = new Container
{
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = left_area_padding },
Children = new Drawable[]
{
beatmapInfoWedge = new BeatmapInfoWedge
@ -200,8 +202,8 @@ namespace osu.Game.Screens.Select
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding
{
Top = left_area_padding,
Right = left_area_padding,
Left = -BeatmapInfoWedge.BORDER_THICKNESS, // Hide the left border
},
},
new Container
@ -210,7 +212,7 @@ namespace osu.Game.Screens.Select
Padding = new MarginPadding
{
Bottom = Footer.HEIGHT,
Top = WEDGE_HEIGHT + left_area_padding,
Top = WEDGE_HEIGHT,
Left = left_area_padding,
Right = left_area_padding * 2,
},