From 03220598b8734cb93d53d15cc054d89e2d4f04ec Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 16 May 2024 07:20:55 +0300 Subject: [PATCH] Move screen footer to `OsuGame` --- .../UserInterface/TestSceneBackButton.cs | 3 +- .../UserInterface/TestSceneScreenFooter.cs | 9 ++-- .../TestSceneScreenFooterButtonMods.cs | 5 ++ osu.Game/OsuGame.cs | 46 +++++++++++++++---- osu.Game/Screens/IOsuScreen.cs | 17 ++++++- .../OnlinePlay/OnlinePlaySongSelect.cs | 4 +- osu.Game/Screens/OsuScreen.cs | 9 ++++ osu.Game/Screens/OsuScreenStack.cs | 6 +++ osu.Game/Screens/Select/SongSelect.cs | 28 +++++------ 9 files changed, 96 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs index 494268b158..7aaf616767 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Footer; using osuTK; using osuTK.Graphics; @@ -15,7 +16,7 @@ namespace osu.Game.Tests.Visual.UserInterface public TestSceneBackButton() { BackButton button; - BackButton.Receptor receptor = new BackButton.Receptor(); + ScreenFooter.BackReceptor receptor = new ScreenFooter.BackReceptor(); Child = new Container { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs index 01b3aa5787..dabb2e7f50 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs @@ -3,7 +3,6 @@ using System; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Testing; @@ -19,9 +18,6 @@ namespace osu.Game.Tests.Visual.UserInterface private ScreenFooter screenFooter = null!; private TestModSelectOverlay overlay = null!; - [Cached] - private OverlayColourProvider colourProvider { get; set; } = new OverlayColourProvider(OverlayColourScheme.Aquamarine); - [SetUp] public void SetUp() => Schedule(() => { @@ -89,6 +85,11 @@ namespace osu.Game.Tests.Visual.UserInterface private partial class TestModSelectOverlay : UserModSelectOverlay { protected override bool ShowPresets => true; + + public TestModSelectOverlay() + : base(OverlayColourScheme.Aquamarine) + { + } } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooterButtonMods.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooterButtonMods.cs index 2e2baf6e96..ba53eb83c4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooterButtonMods.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooterButtonMods.cs @@ -116,6 +116,11 @@ namespace osu.Game.Tests.Visual.UserInterface private partial class TestModSelectOverlay : UserModSelectOverlay { protected override bool ShowPresets => true; + + public TestModSelectOverlay() + : base(OverlayColourScheme.Aquamarine) + { + } } private partial class TestScreenFooterButtonMods : ScreenFooterButtonMods diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index af01a1b1ac..ee1d262f88 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -22,6 +22,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Framework.Input.Bindings; @@ -58,6 +59,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Edit; +using osu.Game.Screens.Footer; using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.Play; @@ -153,6 +155,8 @@ namespace osu.Game private float toolbarOffset => (Toolbar?.Position.Y ?? 0) + (Toolbar?.DrawHeight ?? 0); + private float screenFooterOffset => (ScreenFooter?.DrawHeight ?? 0) - (ScreenFooter?.Position.Y ?? 0); + private IdleTracker idleTracker; /// @@ -177,6 +181,7 @@ namespace osu.Game protected OsuScreenStack ScreenStack; protected BackButton BackButton; + protected ScreenFooter ScreenFooter; protected SettingsOverlay Settings; @@ -917,7 +922,7 @@ namespace osu.Game }; Container logoContainer; - BackButton.Receptor receptor; + ScreenFooter.BackReceptor backReceptor; dependencies.CacheAs(idleTracker = new GameIdleTracker(6000)); @@ -950,20 +955,28 @@ namespace osu.Game Origin = Anchor.Centre, Children = new Drawable[] { - receptor = new BackButton.Receptor(), + backReceptor = new ScreenFooter.BackReceptor(), ScreenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }, - BackButton = new BackButton(receptor) + BackButton = new BackButton(backReceptor) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Action = () => + Action = () => ScreenFooter.OnBack?.Invoke(), + }, + new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Child = ScreenFooter = new ScreenFooter(backReceptor) { - if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen)) - return; + OnBack = () => + { + if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen)) + return; - if (!((Drawable)currentScreen).IsLoaded || (currentScreen.AllowBackButton && !currentScreen.OnBackButton())) - ScreenStack.Exit(); - } + if (!((Drawable)currentScreen).IsLoaded || (currentScreen.AllowBackButton && !currentScreen.OnBackButton())) + ScreenStack.Exit(); + } + }, }, logoContainer = new Container { RelativeSizeAxes = Axes.Both }, } @@ -985,6 +998,8 @@ namespace osu.Game new ConfineMouseTracker() }); + dependencies.Cache(ScreenFooter); + ScreenStack.ScreenPushed += screenPushed; ScreenStack.ScreenExited += screenExited; @@ -1457,6 +1472,7 @@ namespace osu.Game ScreenOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset }; overlayOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset }; + ScreenStack.Padding = new MarginPadding { Bottom = screenFooterOffset }; float horizontalOffset = 0f; @@ -1529,6 +1545,18 @@ namespace osu.Game BackButton.Show(); else BackButton.Hide(); + + if (newOsuScreen.ShowFooter) + { + BackButton.Hide(); + ScreenFooter.SetButtons(newOsuScreen.CreateFooterButtons()); + ScreenFooter.Show(); + } + else + { + ScreenFooter.SetButtons(Array.Empty()); + ScreenFooter.Hide(); + } } skinEditor.SetTarget((OsuScreen)newScreen); diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index 5b4e2d75f4..b80c1f87a4 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -1,11 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Bindables; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Rulesets; +using osu.Game.Screens.Footer; using osu.Game.Users; namespace osu.Game.Screens @@ -19,10 +21,18 @@ namespace osu.Game.Screens bool DisallowExternalBeatmapRulesetChanges { get; } /// - /// Whether the user can exit this this by pressing the back button. + /// Whether the user can exit this by pressing the back button. /// bool AllowBackButton { get; } + /// + /// Whether a footer (and a back button) should be displayed underneath the screen. + /// + /// + /// Temporarily, the back button is shown regardless of whether is true. + /// + bool ShowFooter { get; } + /// /// Whether a top-level component should be allowed to exit the current screen to, for example, /// complete an import. Note that this can be overridden by a user if they specifically request. @@ -63,6 +73,11 @@ namespace osu.Game.Screens Bindable Ruleset { get; } + /// + /// A list of footer buttons to be added to the game footer, or empty to display no buttons. + /// + IReadOnlyList CreateFooterButtons(); + /// /// Whether mod track adjustments should be applied on entering this screen. /// A value means that the parent screen's value of this setting will be used. diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 622ffddba6..a8dfece916 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -173,9 +173,9 @@ namespace osu.Game.Screens.OnlinePlay IsValidMod = IsValidMod }; - protected override IEnumerable<(FooterButton, OverlayContainer?)> CreateFooterButtons() + protected override IEnumerable<(FooterButton, OverlayContainer?)> CreateSongSelectFooterButtons() { - var baseButtons = base.CreateFooterButtons().ToList(); + var baseButtons = base.CreateSongSelectFooterButtons().ToList(); var freeModsButton = new FooterButtonFreeMods(freeModSelectOverlay) { Current = FreeMods }; baseButtons.Insert(baseButtons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (freeModsButton, freeModSelectOverlay)); diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 2e8f85423d..695a074907 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -16,6 +16,7 @@ using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Footer; using osu.Game.Screens.Menu; using osu.Game.Users; @@ -38,6 +39,8 @@ namespace osu.Game.Screens public virtual bool AllowBackButton => true; + public virtual bool ShowFooter => false; + public virtual bool AllowExternalScreenChange => false; public virtual bool HideOverlaysOnEnter => false; @@ -141,6 +144,10 @@ namespace osu.Game.Screens [Resolved(canBeNull: true)] private OsuLogo logo { get; set; } + [Resolved(canBeNull: true)] + [CanBeNull] + protected ScreenFooter Footer { get; private set; } + protected OsuScreen() { Anchor = Anchor.Centre; @@ -298,6 +305,8 @@ namespace osu.Game.Screens /// protected virtual BackgroundScreen CreateBackground() => null; + public virtual IReadOnlyList CreateFooterButtons() => Array.Empty(); + public virtual bool OnBackButton() => false; } } diff --git a/osu.Game/Screens/OsuScreenStack.cs b/osu.Game/Screens/OsuScreenStack.cs index 7d1f6419ad..7103fd6466 100644 --- a/osu.Game/Screens/OsuScreenStack.cs +++ b/osu.Game/Screens/OsuScreenStack.cs @@ -17,6 +17,12 @@ namespace osu.Game.Screens protected float ParallaxAmount => parallaxContainer.ParallaxAmount; + public new MarginPadding Padding + { + get => base.Padding; + set => base.Padding = value; + } + public OsuScreenStack() { InternalChild = parallaxContainer = new ParallaxContainer diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 6225534e95..4b0084c7c5 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -60,19 +60,19 @@ namespace osu.Game.Screens.Select /// protected virtual bool ControlGlobalMusic => true; - protected virtual bool ShowFooter => true; + protected virtual bool ShowSongSelectFooter => true; public override bool? ApplyModTrackAdjustments => true; /// - /// Can be null if is false. + /// Can be null if is false. /// protected BeatmapOptionsOverlay BeatmapOptions { get; private set; } = null!; /// - /// Can be null if is false. + /// Can be null if is false. /// - protected Footer? Footer { get; private set; } + protected Footer? SongSelectFooter { get; private set; } /// /// Contains any panel which is triggered by a footer button. @@ -163,7 +163,7 @@ namespace osu.Game.Screens.Select Origin = Anchor.CentreRight, RelativeSizeAxes = Axes.Both, BleedTop = FilterControl.HEIGHT, - BleedBottom = Footer.HEIGHT, + BleedBottom = Select.Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, FilterApplied = () => Scheduler.AddOnce(updateVisibleBeatmapCount), @@ -210,7 +210,7 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Top = FilterControl.HEIGHT, - Bottom = Footer.HEIGHT + Bottom = Select.Footer.HEIGHT }, Child = new LoadingSpinner(true) { State = { Value = Visibility.Visible } } } @@ -297,7 +297,7 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { - Bottom = Footer.HEIGHT, + Bottom = Select.Footer.HEIGHT, Top = WEDGE_HEIGHT + 70, Left = left_area_padding, Right = left_area_padding * 2, @@ -321,7 +321,7 @@ namespace osu.Game.Screens.Select }, }); - if (ShowFooter) + if (ShowSongSelectFooter) { AddRangeInternal(new Drawable[] { @@ -330,13 +330,13 @@ namespace osu.Game.Screens.Select Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Bottom = Footer.HEIGHT }, + Padding = new MarginPadding { Bottom = Select.Footer.HEIGHT }, Children = new Drawable[] { BeatmapOptions = new BeatmapOptionsOverlay(), } }, - Footer = new Footer() + SongSelectFooter = new Footer() }); } @@ -344,10 +344,10 @@ namespace osu.Game.Screens.Select // therein it will be registered at the `OsuGame` level to properly function as a blocking overlay. LoadComponent(ModSelect = CreateModSelectOverlay()); - if (Footer != null) + if (SongSelectFooter != null) { - foreach (var (button, overlay) in CreateFooterButtons()) - Footer.AddButton(button, overlay); + foreach (var (button, overlay) in CreateSongSelectFooterButtons()) + SongSelectFooter.AddButton(button, overlay); BeatmapOptions.AddButton(@"Manage", @"collections", FontAwesome.Solid.Book, colours.Green, () => manageCollectionsDialog?.Show()); BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => DeleteBeatmap(Beatmap.Value.BeatmapSetInfo)); @@ -381,7 +381,7 @@ namespace osu.Game.Screens.Select /// Creates the buttons to be displayed in the footer. /// /// A set of and an optional which the button opens when pressed. - protected virtual IEnumerable<(FooterButton, OverlayContainer?)> CreateFooterButtons() => new (FooterButton, OverlayContainer?)[] + protected virtual IEnumerable<(FooterButton, OverlayContainer?)> CreateSongSelectFooterButtons() => new (FooterButton, OverlayContainer?)[] { (new FooterButtonMods { Current = Mods }, ModSelect), (new FooterButtonRandom