From 467d7c4f54fc833be0d92bc941db11224155b787 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 29 Jun 2024 10:17:40 +0300 Subject: [PATCH 01/15] Refactor game-wide layout order of footer to fix depth issues with overlays and improve UX With this new order, the logo can be easily moved to display in front of the footer in `SongSelectV2` without breaking experience when footer-based overlays are present. Such overlays (i.e. mod select overlay) will also be dimmed alongside the current screen when a game-wide overlay is open (e.g. settings). --- .../SongSelect/TestSceneSongSelectV2.cs | 6 --- osu.Game/OsuGame.cs | 31 ++++++++++---- .../Overlays/Mods/ShearedOverlayContainer.cs | 24 +++++++---- osu.Game/Screens/Footer/ScreenFooter.cs | 33 ++++++++++++--- osu.Game/Screens/SelectV2/SongSelectV2.cs | 41 ++++++++----------- 5 files changed, 82 insertions(+), 53 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectV2.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectV2.cs index 674eaa2ff82a..0a632793cc2c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectV2.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectV2.cs @@ -181,12 +181,6 @@ public void TestOverlayPresent() #endregion - protected override void Update() - { - base.Update(); - Stack.Padding = new MarginPadding { Bottom = screenScreenFooter.DrawHeight - screenScreenFooter.Y }; - } - private void updateFooter(IScreen? _, IScreen? newScreen) { if (newScreen is IOsuScreen osuScreen && osuScreen.ShowFooter) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index cf32daab00ff..3862fea0e22c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -51,6 +51,7 @@ using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; +using osu.Game.Overlays.Mods; using osu.Game.Overlays.Music; using osu.Game.Overlays.Notifications; using osu.Game.Overlays.SkinEditor; @@ -132,8 +133,12 @@ public partial class OsuGame : OsuGameBase, IKeyBindingHandler, IL private Container topMostOverlayContent; + private Container footerBasedOverlayContent; + protected ScalingContainer ScreenContainer { get; private set; } + private Container logoContainer; + protected Container ScreenOffsetContainer { get; private set; } private Container overlayOffsetContainer; @@ -156,8 +161,6 @@ public partial class OsuGame : OsuGameBase, IKeyBindingHandler, IL private float toolbarOffset => (Toolbar?.Position.Y ?? 0) + (Toolbar?.DrawHeight ?? 0); - private float screenFooterOffset => (ScreenFooter?.DrawHeight ?? 0) - (ScreenFooter?.Position.Y ?? 0); - private IdleTracker idleTracker; /// @@ -242,7 +245,11 @@ IDisposable IOverlayManager.RegisterBlockingOverlay(OverlayContainer overlayCont throw new ArgumentException($@"{overlayContainer} has already been registered via {nameof(IOverlayManager.RegisterBlockingOverlay)} once."); externalOverlays.Add(overlayContainer); - overlayContent.Add(overlayContainer); + + if (overlayContainer is ShearedOverlayContainer) + footerBasedOverlayContent.Add(overlayContainer); + else + overlayContent.Add(overlayContainer); if (overlayContainer is OsuFocusedOverlayContainer focusedOverlayContainer) focusedOverlays.Add(focusedOverlayContainer); @@ -290,6 +297,8 @@ public void CloseAllOverlays(bool hideToolbar = true) if (hideToolbar) Toolbar.Hide(); } + public void ChangeLogoDepth(bool inFrontOfFooter) => ScreenContainer.ChangeChildDepth(logoContainer, inFrontOfFooter ? float.MinValue : 0); + protected override UserInputManager CreateUserInputManager() { var userInputManager = base.CreateUserInputManager(); @@ -934,7 +943,6 @@ protected override void LoadComplete() return string.Join(" / ", combinations); }; - Container logoContainer; ScreenFooter.BackReceptor backReceptor; dependencies.CacheAs(idleTracker = new GameIdleTracker(6000)); @@ -976,8 +984,15 @@ protected override void LoadComplete() Origin = Anchor.BottomLeft, Action = () => ScreenFooter.OnBack?.Invoke(), }, + logoContainer = new Container { RelativeSizeAxes = Axes.Both }, + footerBasedOverlayContent = new Container + { + Depth = -1, + RelativeSizeAxes = Axes.Both, + }, new PopoverContainer { + Depth = -1, RelativeSizeAxes = Axes.Both, Child = ScreenFooter = new ScreenFooter(backReceptor) { @@ -991,7 +1006,6 @@ protected override void LoadComplete() } }, }, - logoContainer = new Container { RelativeSizeAxes = Axes.Both }, } }, } @@ -1025,7 +1039,7 @@ protected override void LoadComplete() if (!IsDeployedBuild) { - dependencies.Cache(versionManager = new VersionManager { Depth = int.MinValue }); + dependencies.Cache(versionManager = new VersionManager()); loadComponentSingleFile(versionManager, ScreenContainer.Add); } @@ -1072,7 +1086,7 @@ protected override void LoadComplete() loadComponentSingleFile(CreateUpdateManager(), Add, true); // overlay elements - loadComponentSingleFile(FirstRunOverlay = new FirstRunSetupOverlay(), overlayContent.Add, true); + loadComponentSingleFile(FirstRunOverlay = new FirstRunSetupOverlay(), footerBasedOverlayContent.Add, true); loadComponentSingleFile(new ManageCollectionsDialog(), overlayContent.Add, true); loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true); loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true); @@ -1137,7 +1151,7 @@ protected override void LoadComplete() } // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { FirstRunOverlay, chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay, wikiOverlay }; + var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay, wikiOverlay }; foreach (var overlay in singleDisplayOverlays) { @@ -1485,7 +1499,6 @@ protected override void UpdateAfterChildren() ScreenOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset }; overlayOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset }; - ScreenStack.Padding = new MarginPadding { Bottom = screenFooterOffset }; float horizontalOffset = 0f; diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index c9c3c6240414..b5435e7e5851 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -1,10 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -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; @@ -30,16 +28,15 @@ public abstract partial class ShearedOverlayContainer : OsuFocusedOverlayContain /// /// The overlay's header. /// - protected ShearedOverlayHeader Header { get; private set; } + protected ShearedOverlayHeader Header { get; private set; } = null!; /// /// The overlay's footer. /// protected Container Footer { get; private set; } - [Resolved(canBeNull: true)] - [CanBeNull] - private ScreenFooter footer { get; set; } + [Resolved] + private ScreenFooter? footer { get; set; } // todo: very temporary property that will be removed once ModSelectOverlay and FirstRunSetupOverlay are updated to use new footer. public virtual bool UseNewFooter => false; @@ -48,12 +45,12 @@ public abstract partial class ShearedOverlayContainer : OsuFocusedOverlayContain /// A container containing all content, including the header and footer. /// May be used for overlay-wide animations. /// - protected Container TopLevelContent { get; private set; } + protected Container TopLevelContent { get; private set; } = null!; /// /// A container for content that is to be displayed between the header and footer. /// - protected Container MainAreaContent { get; private set; } + protected Container MainAreaContent { get; private set; } = null!; /// /// A container for content that is to be displayed inside the footer. @@ -64,6 +61,10 @@ public abstract partial class ShearedOverlayContainer : OsuFocusedOverlayContain protected override bool BlockNonPositionalInput => true; + // ShearedOverlayContainers are placed at a layer within the screen container as they rely on ScreenFooter which must be placed there. + // Therefore, dimming must be managed locally, since DimMainContent dims the entire screen layer. + protected sealed override bool DimMainContent => false; + protected ShearedOverlayContainer(OverlayColourScheme colourScheme) { RelativeSizeAxes = Axes.Both; @@ -81,6 +82,11 @@ private void load() RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourProvider.Background6.Opacity(0.75f), + }, Header = new ShearedOverlayHeader { Anchor = Anchor.TopCentre, diff --git a/osu.Game/Screens/Footer/ScreenFooter.cs b/osu.Game/Screens/Footer/ScreenFooter.cs index cef891f8c052..dcf64e92915b 100644 --- a/osu.Game/Screens/Footer/ScreenFooter.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Threading; using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; using osu.Game.Overlays; @@ -24,6 +25,7 @@ public partial class ScreenFooter : OverlayContainer { private const int padding = 60; private const float delay_per_button = 30; + private const double transition_duration = 400; public const int HEIGHT = 50; @@ -37,6 +39,9 @@ public partial class ScreenFooter : OverlayContainer [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + [Resolved] + private OsuGame? game { get; set; } + public ScreenBackButton BackButton { get; private set; } = null!; public Action? OnBack; @@ -101,19 +106,35 @@ private void load() }; } - public void StartTrackingLogo(OsuLogo logo, float duration = 0, Easing easing = Easing.None) => logoTrackingContainer.StartTracking(logo, duration, easing); - public void StopTrackingLogo() => logoTrackingContainer.StopTracking(); + private ScheduledDelegate? changeLogoDepthDelegate; + + public void StartTrackingLogo(OsuLogo logo, float duration = 0, Easing easing = Easing.None) + { + changeLogoDepthDelegate?.Cancel(); + changeLogoDepthDelegate = null; + + logoTrackingContainer.StartTracking(logo, duration, easing); + game?.ChangeLogoDepth(inFrontOfFooter: true); + } + + public void StopTrackingLogo() + { + logoTrackingContainer.StopTracking(); + + if (game != null) + changeLogoDepthDelegate = Scheduler.AddDelayed(() => game.ChangeLogoDepth(inFrontOfFooter: false), transition_duration); + } protected override void PopIn() { - this.MoveToY(0, 400, Easing.OutQuint) - .FadeIn(400, Easing.OutQuint); + this.MoveToY(0, transition_duration, Easing.OutQuint) + .FadeIn(transition_duration, Easing.OutQuint); } protected override void PopOut() { - this.MoveToY(HEIGHT, 400, Easing.OutQuint) - .FadeOut(400, Easing.OutQuint); + this.MoveToY(HEIGHT, transition_duration, Easing.OutQuint) + .FadeOut(transition_duration, Easing.OutQuint); } public void SetButtons(IReadOnlyList buttons) diff --git a/osu.Game/Screens/SelectV2/SongSelectV2.cs b/osu.Game/Screens/SelectV2/SongSelectV2.cs index 10ed7783c4a7..a8730ad80858 100644 --- a/osu.Game/Screens/SelectV2/SongSelectV2.cs +++ b/osu.Game/Screens/SelectV2/SongSelectV2.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Cursor; using osu.Framework.Screens; using osu.Game.Overlays; using osu.Game.Overlays.Mods; @@ -23,6 +22,8 @@ namespace osu.Game.Screens.SelectV2 /// public partial class SongSelectV2 : OsuScreen { + private const float logo_scale = 0.4f; + private readonly ModSelectOverlay modSelectOverlay = new SoloModSelectOverlay(); [Cached] @@ -30,15 +31,14 @@ public partial class SongSelectV2 : OsuScreen public override bool ShowFooter => true; + [Resolved] + private OsuLogo? logo { get; set; } + [BackgroundDependencyLoader] private void load() { AddRangeInternal(new Drawable[] { - new PopoverContainer - { - RelativeSizeAxes = Axes.Both, - }, modSelectOverlay, }); } @@ -50,6 +50,17 @@ private void load() new ScreenFooterButtonOptions(), }; + protected override void LoadComplete() + { + base.LoadComplete(); + + modSelectOverlay.State.BindValueChanged(v => + { + logo?.ScaleTo(v.NewValue == Visibility.Visible ? 0f : logo_scale, 400, Easing.OutQuint) + .FadeTo(v.NewValue == Visibility.Visible ? 0f : 1f, 200, Easing.OutQuint); + }, true); + } + public override void OnEntering(ScreenTransitionEvent e) { this.FadeIn(); @@ -74,17 +85,6 @@ public override bool OnExiting(ScreenExitEvent e) return base.OnExiting(e); } - public override bool OnBackButton() - { - if (modSelectOverlay.State.Value == Visibility.Visible) - { - modSelectOverlay.Hide(); - return true; - } - - return false; - } - protected override void LogoArriving(OsuLogo logo, bool resuming) { base.LogoArriving(logo, resuming); @@ -99,7 +99,7 @@ protected override void LogoArriving(OsuLogo logo, bool resuming) } logo.FadeIn(240, Easing.OutQuint); - logo.ScaleTo(0.4f, 240, Easing.OutQuint); + logo.ScaleTo(logo_scale, 240, Easing.OutQuint); logo.Action = () => { @@ -122,14 +122,9 @@ protected override void LogoExiting(OsuLogo logo) logo.FadeOut(120, Easing.Out); } - private partial class SoloModSelectOverlay : ModSelectOverlay + private partial class SoloModSelectOverlay : UserModSelectOverlay { protected override bool ShowPresets => true; - - public SoloModSelectOverlay() - : base(OverlayColourScheme.Aquamarine) - { - } } private partial class PlayerLoaderV2 : PlayerLoader From 48bf3f1385e6b99fd5bd0e8104d40487b22c781a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 29 Jun 2024 08:38:43 +0300 Subject: [PATCH 02/15] Migrate mod select overlay footer content --- .../TestSceneModSelectOverlay.cs | 39 ++-- .../Overlays/Mods/ModSelectFooterContent.cs | 177 ++++++++++++++++++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 165 ++-------------- .../OnlinePlay/FreeModSelectOverlay.cs | 41 +++- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- .../Tests/Visual/Gameplay/ScoringTestScene.cs | 1 - 7 files changed, 257 insertions(+), 170 deletions(-) create mode 100644 osu.Game/Overlays/Mods/ModSelectFooterContent.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index dedad3a40ac7..8b79320ffb8e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -24,6 +24,7 @@ using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Screens.Footer; using osu.Game.Tests.Mods; using osuTK; using osuTK.Input; @@ -93,12 +94,28 @@ public void SetUpSteps() private void createScreen() { - AddStep("create screen", () => Child = modSelectOverlay = new TestModSelectOverlay + AddStep("create screen", () => { - RelativeSizeAxes = Axes.Both, - State = { Value = Visibility.Visible }, - Beatmap = Beatmap.Value, - SelectedMods = { BindTarget = SelectedMods } + var receptor = new ScreenFooter.BackReceptor(); + var footer = new ScreenFooter(receptor); + + Child = new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new[] { (typeof(ScreenFooter), (object)footer) }, + Children = new Drawable[] + { + receptor, + modSelectOverlay = new TestModSelectOverlay + { + RelativeSizeAxes = Axes.Both, + State = { Value = Visibility.Visible }, + Beatmap = { Value = Beatmap.Value }, + SelectedMods = { BindTarget = SelectedMods }, + }, + footer, + } + }; }); waitForColumnLoad(); } @@ -119,7 +136,7 @@ public void TestPreexistingSelection() AddAssert("mod multiplier correct", () => { double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); - return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().ModMultiplier.Value); + return Precision.AlmostEquals(multiplier, this.ChildrenOfType().Single().ModMultiplier.Value); }); assertCustomisationToggleState(disabled: false, active: false); AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType().Any()); @@ -134,7 +151,7 @@ public void TestExternalSelection() AddAssert("mod multiplier correct", () => { double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); - return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().ModMultiplier.Value); + return Precision.AlmostEquals(multiplier, this.ChildrenOfType().Single().ModMultiplier.Value); }); assertCustomisationToggleState(disabled: false, active: false); AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType().Any()); @@ -756,7 +773,7 @@ public void TestCloseViaBackButton() AddStep("click back button", () => { - InputManager.MoveMouseTo(modSelectOverlay.BackButton); + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); InputManager.Click(MouseButton.Left); }); AddAssert("mod select hidden", () => modSelectOverlay.State.Value == Visibility.Hidden); @@ -884,7 +901,7 @@ public void TestModMultiplierUpdates() InputManager.Click(MouseButton.Left); }); AddAssert("difficulty multiplier display shows correct value", - () => modSelectOverlay.ChildrenOfType().Single().ModMultiplier.Value, () => Is.EqualTo(0.1).Within(Precision.DOUBLE_EPSILON)); + () => this.ChildrenOfType().Single().ModMultiplier.Value, () => Is.EqualTo(0.1).Within(Precision.DOUBLE_EPSILON)); // this is highly unorthodox in a test, but because the `ModSettingChangeTracker` machinery heavily leans on events and object disposal and re-creation, // it is instrumental in the reproduction of the failure scenario that this test is supposed to cover. @@ -894,7 +911,7 @@ public void TestModMultiplierUpdates() AddStep("reset half time speed to default", () => modSelectOverlay.ChildrenOfType().Single() .ChildrenOfType>().Single().TriggerClick()); AddUntilStep("difficulty multiplier display shows correct value", - () => modSelectOverlay.ChildrenOfType().Single().ModMultiplier.Value, () => Is.EqualTo(0.3).Within(Precision.DOUBLE_EPSILON)); + () => this.ChildrenOfType().Single().ModMultiplier.Value, () => Is.EqualTo(0.3).Within(Precision.DOUBLE_EPSILON)); } [Test] @@ -1014,8 +1031,6 @@ private ModPanel getPanelForMod(Type modType) private partial class TestModSelectOverlay : UserModSelectOverlay { protected override bool ShowPresets => true; - - public new ShearedButton BackButton => base.BackButton; } private class TestUnimplementedMod : Mod diff --git a/osu.Game/Overlays/Mods/ModSelectFooterContent.cs b/osu.Game/Overlays/Mods/ModSelectFooterContent.cs new file mode 100644 index 000000000000..146b8e4ebe92 --- /dev/null +++ b/osu.Game/Overlays/Mods/ModSelectFooterContent.cs @@ -0,0 +1,177 @@ +// 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 System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Mods; +using osuTK; + +namespace osu.Game.Overlays.Mods +{ + public partial class ModSelectFooterContent : VisibilityContainer + { + private readonly ModSelectOverlay overlay; + + private RankingInformationDisplay? rankingInformationDisplay; + private BeatmapAttributesDisplay? beatmapAttributesDisplay; + private FillFlowContainer buttonFlow = null!; + private FillFlowContainer contentFlow = null!; + + public DeselectAllModsButton? DeselectAllModsButton { get; set; } + + public readonly IBindable Beatmap = new Bindable(); + public readonly IBindable> ActiveMods = new Bindable>(); + + /// + /// Whether the effects (on score multiplier, on or beatmap difficulty) of the current selected set of mods should be shown. + /// + protected virtual bool ShowModEffects => true; + + /// + /// Whether the ranking information and beatmap attributes displays are stacked vertically due to small space. + /// + public bool DisplaysStackedVertically { get; private set; } + + public ModSelectFooterContent(ModSelectOverlay overlay) + { + this.overlay = overlay; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + InternalChild = buttonFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Padding = new MarginPadding { Horizontal = 20 }, + Spacing = new Vector2(10), + ChildrenEnumerable = CreateButtons(), + }; + + if (ShowModEffects) + { + AddInternal(contentFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(30, 10), + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding { Horizontal = 20 }, + Children = new Drawable[] + { + rankingInformationDisplay = new RankingInformationDisplay + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight + }, + beatmapAttributesDisplay = new BeatmapAttributesDisplay + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + BeatmapInfo = { Value = Beatmap.Value?.BeatmapInfo }, + }, + } + }); + } + } + + private ModSettingChangeTracker? modSettingChangeTracker; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Beatmap.BindValueChanged(b => + { + if (beatmapAttributesDisplay != null) + beatmapAttributesDisplay.BeatmapInfo.Value = b.NewValue?.BeatmapInfo; + }, true); + + ActiveMods.BindValueChanged(m => + { + updateInformation(); + + modSettingChangeTracker?.Dispose(); + + // Importantly, use ActiveMods.Value here (and not the ValueChanged NewValue) as the latter can + // potentially be stale, due to complexities in the way change trackers work. + // + // See https://github.com/ppy/osu/pull/23284#issuecomment-1529056988 + modSettingChangeTracker = new ModSettingChangeTracker(ActiveMods.Value); + modSettingChangeTracker.SettingChanged += _ => updateInformation(); + }, true); + } + + private void updateInformation() + { + if (rankingInformationDisplay != null) + { + double multiplier = 1.0; + + foreach (var mod in ActiveMods.Value) + multiplier *= mod.ScoreMultiplier; + + rankingInformationDisplay.ModMultiplier.Value = multiplier; + rankingInformationDisplay.Ranked.Value = ActiveMods.Value.All(m => m.Ranked); + } + + if (beatmapAttributesDisplay != null) + beatmapAttributesDisplay.Mods.Value = ActiveMods.Value; + } + + protected override void Update() + { + base.Update(); + + if (beatmapAttributesDisplay != null) + { + float rightEdgeOfLastButton = buttonFlow[^1].ScreenSpaceDrawQuad.TopRight.X; + + // this is cheating a bit; the 640 value is hardcoded based on how wide the expanded panel _generally_ is. + // due to the transition applied, the raw screenspace quad of the panel cannot be used, as it will trigger an ugly feedback cycle of expanding and collapsing. + float projectedLeftEdgeOfExpandedBeatmapAttributesDisplay = buttonFlow.ToScreenSpace(buttonFlow.DrawSize - new Vector2(640, 0)).X; + + DisplaysStackedVertically = rightEdgeOfLastButton > projectedLeftEdgeOfExpandedBeatmapAttributesDisplay; + + // only update preview panel's collapsed state after we are fully visible, to ensure all the buttons are where we expect them to be. + if (Alpha == 1) + beatmapAttributesDisplay.Collapsed.Value = DisplaysStackedVertically; + + contentFlow.LayoutDuration = 200; + contentFlow.LayoutEasing = Easing.OutQuint; + contentFlow.Direction = DisplaysStackedVertically ? FillDirection.Vertical : FillDirection.Horizontal; + } + } + + protected virtual IEnumerable CreateButtons() => new[] + { + DeselectAllModsButton = new DeselectAllModsButton(overlay) + }; + + protected override void PopIn() + { + this.MoveToY(0, 400, Easing.OutQuint) + .FadeIn(400, Easing.OutQuint); + } + + protected override void PopOut() + { + this.MoveToY(-20f, 200, Easing.OutQuint) + .FadeOut(200, Easing.OutQuint); + } + } +} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index b0d58480db78..40be4e08a628 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -27,6 +27,7 @@ using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Footer; using osu.Game.Utils; using osuTK; using osuTK.Graphics; @@ -87,11 +88,6 @@ public string SearchTerm public ShearedSearchTextBox SearchTextBox { get; private set; } = null!; - /// - /// Whether the effects (on score multiplier, on or beatmap difficulty) of the current selected set of mods should be shown. - /// - protected virtual bool ShowModEffects => true; - /// /// Whether per-mod customisation controls are visible. /// @@ -108,11 +104,6 @@ public string SearchTerm protected virtual IReadOnlyList ComputeActiveMods() => SelectedMods.Value; - protected virtual IEnumerable CreateFooterButtons() - { - yield return deselectAllModsButton = new DeselectAllModsButton(this); - } - private readonly Bindable>> globalAvailableMods = new Bindable>>(); public IEnumerable AllAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value); @@ -121,34 +112,18 @@ protected virtual IEnumerable CreateFooterButtons() private ColumnScrollContainer columnScroll = null!; private ColumnFlowContainer columnFlow = null!; - private FillFlowContainer footerButtonFlow = null!; - private FillFlowContainer footerContentFlow = null!; - private DeselectAllModsButton deselectAllModsButton = null!; private Container aboveColumnsContent = null!; - private RankingInformationDisplay? rankingInformationDisplay; - private BeatmapAttributesDisplay? beatmapAttributesDisplay; private ModCustomisationPanel customisationPanel = null!; - protected ShearedButton BackButton { get; private set; } = null!; - protected SelectAllModsButton? SelectAllModsButton { get; set; } + protected virtual SelectAllModsButton? SelectAllModsButton => null; private Sample? columnAppearSample; - private WorkingBeatmap? beatmap; - - public WorkingBeatmap? Beatmap - { - get => beatmap; - set - { - if (beatmap == value) return; + public readonly Bindable Beatmap = new Bindable(); - beatmap = value; - if (IsLoaded && beatmapAttributesDisplay != null) - beatmapAttributesDisplay.BeatmapInfo.Value = beatmap?.BeatmapInfo; - } - } + [Resolved] + private ScreenFooter? footer { get; set; } protected ModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green) : base(colourScheme) @@ -226,59 +201,6 @@ private void load(OsuGameBase game, OsuColour colours, AudioManager audio, OsuCo } }); - FooterContent.Add(footerButtonFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Padding = new MarginPadding - { - Vertical = PADDING, - Horizontal = 70 - }, - Spacing = new Vector2(10), - ChildrenEnumerable = CreateFooterButtons().Prepend(BackButton = new ShearedButton(BUTTON_WIDTH) - { - Text = CommonStrings.Back, - Action = Hide, - DarkerColour = colours.Pink2, - LighterColour = colours.Pink1 - }) - }); - - if (ShowModEffects) - { - FooterContent.Add(footerContentFlow = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(30, 10), - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Margin = new MarginPadding - { - Vertical = PADDING, - Horizontal = 20 - }, - Children = new Drawable[] - { - rankingInformationDisplay = new RankingInformationDisplay - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight - }, - beatmapAttributesDisplay = new BeatmapAttributesDisplay - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - BeatmapInfo = { Value = Beatmap?.BeatmapInfo }, - }, - } - }); - } - globalAvailableMods.BindTo(game.AvailableMods); textSearchStartsActive = configManager.GetBindable(OsuSetting.ModSelectTextSearchStartsActive); @@ -292,8 +214,6 @@ public override void Hide() SearchTextBox.Current.Value = string.Empty; } - private ModSettingChangeTracker? modSettingChangeTracker; - protected override void LoadComplete() { // this is called before base call so that the mod state is populated early, and the transition in `PopIn()` can play out properly. @@ -316,23 +236,6 @@ protected override void LoadComplete() ActiveMods.Value = ComputeActiveMods(); }, true); - ActiveMods.BindValueChanged(_ => - { - updateOverlayInformation(); - - modSettingChangeTracker?.Dispose(); - - if (AllowCustomisation) - { - // Importantly, use ActiveMods.Value here (and not the ValueChanged NewValue) as the latter can - // potentially be stale, due to complexities in the way change trackers work. - // - // See https://github.com/ppy/osu/pull/23284#issuecomment-1529056988 - modSettingChangeTracker = new ModSettingChangeTracker(ActiveMods.Value); - modSettingChangeTracker.SettingChanged += _ => updateOverlayInformation(); - } - }, true); - customisationPanel.Expanded.BindValueChanged(_ => updateCustomisationVisualState(), true); SearchTextBox.Current.BindValueChanged(query => @@ -350,6 +253,16 @@ protected override void LoadComplete() }); } + private ModSelectFooterContent? currentFooterContent; + + public override bool UseNewFooter => true; + + public override Drawable CreateFooterContent() => currentFooterContent = new ModSelectFooterContent(this) + { + Beatmap = { BindTarget = Beatmap }, + ActiveMods = { BindTarget = ActiveMods }, + }; + private static readonly LocalisableString input_search_placeholder = Resources.Localisation.Web.CommonStrings.InputSearch; private static readonly LocalisableString tab_to_search_placeholder = ModSelectOverlayStrings.TabToSearch; @@ -358,26 +271,7 @@ protected override void Update() base.Update(); SearchTextBox.PlaceholderText = SearchTextBox.HasFocus ? input_search_placeholder : tab_to_search_placeholder; - - if (beatmapAttributesDisplay != null) - { - float rightEdgeOfLastButton = footerButtonFlow[^1].ScreenSpaceDrawQuad.TopRight.X; - - // this is cheating a bit; the 640 value is hardcoded based on how wide the expanded panel _generally_ is. - // due to the transition applied, the raw screenspace quad of the panel cannot be used, as it will trigger an ugly feedback cycle of expanding and collapsing. - float projectedLeftEdgeOfExpandedBeatmapAttributesDisplay = footerButtonFlow.ToScreenSpace(footerButtonFlow.DrawSize - new Vector2(640, 0)).X; - - bool screenIsntWideEnough = rightEdgeOfLastButton > projectedLeftEdgeOfExpandedBeatmapAttributesDisplay; - - // only update preview panel's collapsed state after we are fully visible, to ensure all the buttons are where we expect them to be. - if (Alpha == 1) - beatmapAttributesDisplay.Collapsed.Value = screenIsntWideEnough; - - footerContentFlow.LayoutDuration = 200; - footerContentFlow.LayoutEasing = Easing.OutQuint; - footerContentFlow.Direction = screenIsntWideEnough ? FillDirection.Vertical : FillDirection.Horizontal; - aboveColumnsContent.Padding = aboveColumnsContent.Padding with { Bottom = screenIsntWideEnough ? 70f : 15f }; - } + aboveColumnsContent.Padding = aboveColumnsContent.Padding with { Bottom = currentFooterContent?.DisplaysStackedVertically == true ? 75f : 15f }; } /// @@ -455,27 +349,6 @@ private void filterMods() modState.ValidForSelection.Value = modState.Mod.Type != ModType.System && modState.Mod.HasImplementation && IsValidMod.Invoke(modState.Mod); } - /// - /// Updates any information displayed on the overlay regarding the effects of the active mods. - /// This reads from instead of . - /// - private void updateOverlayInformation() - { - if (rankingInformationDisplay != null) - { - double multiplier = 1.0; - - foreach (var mod in ActiveMods.Value) - multiplier *= mod.ScoreMultiplier; - - rankingInformationDisplay.ModMultiplier.Value = multiplier; - rankingInformationDisplay.Ranked.Value = ActiveMods.Value.All(m => m.Ranked); - } - - if (beatmapAttributesDisplay != null) - beatmapAttributesDisplay.Mods.Value = ActiveMods.Value; - } - private void updateCustomisation() { if (!AllowCustomisation) @@ -701,7 +574,7 @@ public override bool OnPressed(KeyBindingPressEvent e) { if (!SearchTextBox.HasFocus && !customisationPanel.Expanded.Value) { - deselectAllModsButton.TriggerClick(); + currentFooterContent?.DeselectAllModsButton?.TriggerClick(); return true; } @@ -732,7 +605,7 @@ public override bool OnPressed(KeyBindingPressEvent e) return base.OnPressed(e); - void hideOverlay() => BackButton.TriggerClick(); + void hideOverlay() => footer?.BackButton.TriggerClick(); } /// @@ -740,7 +613,7 @@ public override bool OnPressed(KeyBindingPressEvent e) /// This is handled locally here due to conflicts in input handling between the search text box and the select all mods button. /// Attempting to handle this action locally in both places leads to a possible scenario /// wherein activating the "select all" platform binding will both select all text in the search box and select all mods. - /// > + /// public bool OnPressed(KeyBindingPressEvent e) { if (e.Repeat || e.Action != PlatformAction.SelectAll || SelectAllModsButton == null) diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 7f090aca5718..0ed45161f24e 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -14,8 +14,6 @@ namespace osu.Game.Screens.OnlinePlay { public partial class FreeModSelectOverlay : ModSelectOverlay { - protected override bool ShowModEffects => false; - protected override bool AllowCustomisation => false; public new Func IsValidMod @@ -24,6 +22,10 @@ public partial class FreeModSelectOverlay : ModSelectOverlay set => base.IsValidMod = m => m.UserPlayable && value.Invoke(m); } + private FreeModSelectFooterContent? currentFooterContent; + + protected override SelectAllModsButton? SelectAllModsButton => currentFooterContent?.SelectAllModsButton; + public FreeModSelectOverlay() : base(OverlayColourScheme.Plum) { @@ -32,12 +34,33 @@ public FreeModSelectOverlay() protected override ModColumn CreateModColumn(ModType modType) => new ModColumn(modType, true); - protected override IEnumerable CreateFooterButtons() - => base.CreateFooterButtons() - .Prepend(SelectAllModsButton = new SelectAllModsButton(this) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - }); + public override Drawable CreateFooterContent() => currentFooterContent = new FreeModSelectFooterContent(this) + { + Beatmap = { BindTarget = Beatmap }, + ActiveMods = { BindTarget = ActiveMods }, + }; + + private partial class FreeModSelectFooterContent : ModSelectFooterContent + { + private readonly FreeModSelectOverlay overlay; + + protected override bool ShowModEffects => false; + + public SelectAllModsButton? SelectAllModsButton; + + public FreeModSelectFooterContent(FreeModSelectOverlay overlay) + : base(overlay) + { + this.overlay = overlay; + } + + protected override IEnumerable CreateButtons() + => base.CreateButtons() + .Prepend(SelectAllModsButton = new SelectAllModsButton(overlay) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + }); + } } } diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 4eb092d08b50..78ab8cfa6cd2 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -453,7 +453,7 @@ private void updateWorkingBeatmap() // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineID == beatmap.OnlineID); - UserModsSelectOverlay.Beatmap = Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); + UserModsSelectOverlay.Beatmap.Value = Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); } protected virtual void UpdateMods() diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index df7eabfd2182..ecf821000243 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -851,7 +851,7 @@ private void updateComponentFromBeatmap(WorkingBeatmap beatmap) BeatmapDetails.Beatmap = beatmap; - ModSelect.Beatmap = beatmap; + ModSelect.Beatmap.Value = beatmap; advancedStats.BeatmapInfo = beatmap.BeatmapInfo; diff --git a/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs b/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs index e7053e42024b..6908f7f1b480 100644 --- a/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs +++ b/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs @@ -658,7 +658,6 @@ private void updateState() private partial class TestModSelectOverlay : UserModSelectOverlay { - protected override bool ShowModEffects => true; protected override bool ShowPresets => false; public TestModSelectOverlay() From 5dd822ea3899eafde0b2beee191243e65378928a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 29 Jun 2024 09:39:09 +0300 Subject: [PATCH 03/15] Migrate first-run setup overlay footer content --- .../TestSceneFirstRunSetupOverlay.cs | 62 ++++--- .../Overlays/FirstRunSetup/ScreenUIScale.cs | 8 +- osu.Game/Overlays/FirstRunSetupOverlay.cs | 165 +++++++++--------- 3 files changed, 127 insertions(+), 108 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs index 51da4d87550b..2ca06bf2f461 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFirstRunSetupOverlay.cs @@ -11,6 +11,8 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; @@ -20,6 +22,7 @@ using osu.Game.Overlays.FirstRunSetup; using osu.Game.Overlays.Notifications; using osu.Game.Screens; +using osu.Game.Screens.Footer; using osuTK; using osuTK.Input; @@ -28,6 +31,7 @@ namespace osu.Game.Tests.Visual.UserInterface public partial class TestSceneFirstRunSetupOverlay : OsuManualInputManagerTestScene { private FirstRunSetupOverlay overlay; + private ScreenFooter footer; private readonly Mock performer = new Mock(); @@ -60,19 +64,16 @@ public void SetUpSteps() .Callback((Notification n) => lastNotification = n); }); - AddStep("add overlay", () => - { - Child = overlay = new FirstRunSetupOverlay - { - State = { Value = Visibility.Visible } - }; - }); + createOverlay(); + + AddStep("show overlay", () => overlay.Show()); } [Test] public void TestBasic() { AddAssert("overlay visible", () => overlay.State.Value == Visibility.Visible); + AddAssert("footer visible", () => footer.State.Value == Visibility.Visible); } [Test] @@ -82,16 +83,13 @@ public void TestDoesntOpenOnSecondRun() AddUntilStep("step through", () => { - if (overlay.CurrentScreen?.IsLoaded != false) overlay.NextButton.TriggerClick(); + if (overlay.CurrentScreen?.IsLoaded != false) overlay.NextButton.AsNonNull().TriggerClick(); return overlay.State.Value == Visibility.Hidden; }); AddAssert("first run false", () => !LocalConfig.Get(OsuSetting.ShowFirstRunSetup)); - AddStep("add overlay", () => - { - Child = overlay = new FirstRunSetupOverlay(); - }); + createOverlay(); AddWaitStep("wait some", 5); @@ -109,7 +107,7 @@ public void TestOverlayRunsToFinish(bool keyboard) if (keyboard) InputManager.Key(Key.Enter); else - overlay.NextButton.TriggerClick(); + overlay.NextButton.AsNonNull().TriggerClick(); } return overlay.State.Value == Visibility.Hidden; @@ -128,11 +126,9 @@ public void TestOverlayRunsToFinish(bool keyboard) [TestCase(true)] public void TestBackButton(bool keyboard) { - AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value); - AddUntilStep("step to last", () => { - var nextButton = overlay.NextButton; + var nextButton = overlay.NextButton.AsNonNull(); if (overlay.CurrentScreen?.IsLoaded != false) nextButton.TriggerClick(); @@ -142,24 +138,29 @@ public void TestBackButton(bool keyboard) AddUntilStep("step back to start", () => { - if (overlay.CurrentScreen?.IsLoaded != false) + if (overlay.CurrentScreen?.IsLoaded != false && !(overlay.CurrentScreen is ScreenWelcome)) { if (keyboard) InputManager.Key(Key.Escape); else - overlay.BackButton.TriggerClick(); + footer.BackButton.TriggerClick(); } return overlay.CurrentScreen is ScreenWelcome; }); - AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value); + AddAssert("overlay not dismissed", () => overlay.State.Value == Visibility.Visible); if (keyboard) { AddStep("exit via keyboard", () => InputManager.Key(Key.Escape)); AddAssert("overlay dismissed", () => overlay.State.Value == Visibility.Hidden); } + else + { + AddStep("press back button", () => footer.BackButton.TriggerClick()); + AddAssert("overlay dismissed", () => overlay.State.Value == Visibility.Hidden); + } } [Test] @@ -185,7 +186,7 @@ public void TestClickAwayToExit() [Test] public void TestResumeViaNotification() { - AddStep("step to next", () => overlay.NextButton.TriggerClick()); + AddStep("step to next", () => overlay.NextButton.AsNonNull().TriggerClick()); AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenUIScale); @@ -200,6 +201,27 @@ public void TestResumeViaNotification() AddAssert("is resumed", () => overlay.CurrentScreen is ScreenUIScale); } + private void createOverlay() + { + AddStep("add overlay", () => + { + var receptor = new ScreenFooter.BackReceptor(); + footer = new ScreenFooter(receptor); + + Child = new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new[] { (typeof(ScreenFooter), (object)footer) }, + Children = new Drawable[] + { + receptor, + overlay = new FirstRunSetupOverlay(), + footer, + } + }; + }); + } + // interface mocks break hot reload, mocking this stub implementation instead works around it. // see: https://github.com/moq/moq4/issues/1252 [UsedImplicitly] diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs index 02f0ad95063f..d0eefa55c543 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenUIScale.cs @@ -23,6 +23,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens; +using osu.Game.Screens.Footer; using osu.Game.Screens.Menu; using osu.Game.Screens.Select; using osu.Game.Tests.Visual; @@ -153,6 +154,7 @@ private void load(AudioManager audio, TextureStore textures, RulesetStore rulese OsuScreenStack stack; OsuLogo logo; + ScreenFooter footer; Padding = new MarginPadding(5); @@ -166,7 +168,8 @@ private void load(AudioManager audio, TextureStore textures, RulesetStore rulese { RelativePositionAxes = Axes.Both, Position = new Vector2(0.5f), - }) + }), + (typeof(ScreenFooter), footer = new ScreenFooter()), }, RelativeSizeAxes = Axes.Both, Children = new Drawable[] @@ -178,7 +181,8 @@ private void load(AudioManager audio, TextureStore textures, RulesetStore rulese Children = new Drawable[] { stack = new OsuScreenStack(), - logo + footer, + logo, }, }, } diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index f2fdaefbb4bf..bc11e5d0d219 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -26,6 +26,7 @@ using osu.Game.Overlays.Mods; using osu.Game.Overlays.Notifications; using osu.Game.Screens; +using osu.Game.Screens.Footer; using osu.Game.Screens.Menu; namespace osu.Game.Overlays @@ -44,8 +45,7 @@ public partial class FirstRunSetupOverlay : ShearedOverlayContainer private ScreenStack? stack; - public ShearedButton NextButton = null!; - public ShearedButton BackButton = null!; + public ShearedButton? NextButton => currentFooterContent?.NextButton; private readonly Bindable showFirstRunSetup = new Bindable(); @@ -90,7 +90,7 @@ private void load(OsuColour colours, LegacyImportManager? legacyImportManager) Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Bottom = 20, }, + Padding = new MarginPadding { Bottom = 20 }, Child = new GridContainer { Anchor = Anchor.Centre, @@ -134,51 +134,6 @@ private void load(OsuColour colours, LegacyImportManager? legacyImportManager) } }, }); - - FooterContent.Add(new GridContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Margin = new MarginPadding { Vertical = PADDING }, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.Absolute, 10), - new Dimension(GridSizeMode.AutoSize), - new Dimension(), - new Dimension(GridSizeMode.Absolute, 10), - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - }, - Content = new[] - { - new[] - { - Empty(), - BackButton = new ShearedButton(300) - { - Text = CommonStrings.Back, - Action = showPreviousStep, - Enabled = { Value = false }, - DarkerColour = colours.Pink2, - LighterColour = colours.Pink1, - }, - NextButton = new ShearedButton(0) - { - RelativeSizeAxes = Axes.X, - Width = 1, - Text = FirstRunSetupOverlayStrings.GetStarted, - DarkerColour = ColourProvider.Colour2, - LighterColour = ColourProvider.Colour1, - Action = showNextStep - }, - Empty(), - }, - } - }); } protected override void LoadComplete() @@ -190,6 +145,32 @@ protected override void LoadComplete() if (showFirstRunSetup.Value) Show(); } + [Resolved] + private ScreenFooter footer { get; set; } = null!; + + private FirstRunSetupFooterContent? currentFooterContent; + + public override bool UseNewFooter => true; + + public override Drawable CreateFooterContent() => currentFooterContent = new FirstRunSetupFooterContent + { + ShowNextStep = showNextStep, + }; + + public override bool OnBackButton() + { + if (currentStepIndex == 0) + return false; + + Debug.Assert(stack != null); + + stack.CurrentScreen.Exit(); + currentStepIndex--; + + updateButtons(); + return true; + } + public override bool OnPressed(KeyBindingPressEvent e) { if (!e.Repeat) @@ -197,19 +178,12 @@ public override bool OnPressed(KeyBindingPressEvent e) switch (e.Action) { case GlobalAction.Select: - NextButton.TriggerClick(); + currentFooterContent?.NextButton.TriggerClick(); return true; case GlobalAction.Back: - if (BackButton.Enabled.Value) - { - BackButton.TriggerClick(); - return true; - } - - // If back button is disabled, we are at the first step. - // The base call will handle dismissal of the overlay. - break; + footer.BackButton.TriggerClick(); + return false; } } @@ -279,19 +253,6 @@ private void showFirstStep() showNextStep(); } - private void showPreviousStep() - { - if (currentStepIndex == 0) - return; - - Debug.Assert(stack != null); - - stack.CurrentScreen.Exit(); - currentStepIndex--; - - updateButtons(); - } - private void showNextStep() { Debug.Assert(currentStepIndex != null); @@ -322,29 +283,61 @@ private void showNextStep() updateButtons(); } - private void updateButtons() + private void updateButtons() => currentFooterContent?.UpdateButtons(currentStepIndex, steps); + + private partial class FirstRunSetupFooterContent : VisibilityContainer { - BackButton.Enabled.Value = currentStepIndex > 0; - NextButton.Enabled.Value = currentStepIndex != null; + public ShearedButton NextButton { get; private set; } = null!; - if (currentStepIndex == null) - return; + public Action? ShowNextStep; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + RelativeSizeAxes = Axes.Both; - bool isFirstStep = currentStepIndex == 0; - bool isLastStep = currentStepIndex == steps.Count - 1; + InternalChild = NextButton = new ShearedButton(0) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Margin = new MarginPadding { Right = 12f }, + RelativeSizeAxes = Axes.X, + Width = 1, + Text = FirstRunSetupOverlayStrings.GetStarted, + DarkerColour = colourProvider.Colour2, + LighterColour = colourProvider.Colour1, + Action = () => ShowNextStep?.Invoke(), + }; + } - if (isFirstStep) + public void UpdateButtons(int? currentStep, IReadOnlyList steps) { - BackButton.Text = CommonStrings.Back; - NextButton.Text = FirstRunSetupOverlayStrings.GetStarted; + NextButton.Enabled.Value = currentStep != null; + + if (currentStep == null) + return; + + bool isFirstStep = currentStep == 0; + bool isLastStep = currentStep == steps.Count - 1; + + if (isFirstStep) + NextButton.Text = FirstRunSetupOverlayStrings.GetStarted; + else + { + NextButton.Text = isLastStep + ? CommonStrings.Finish + : LocalisableString.Interpolate($@"{CommonStrings.Next} ({steps[currentStep.Value + 1].GetLocalisableDescription()})"); + } } - else + + protected override void PopIn() { - BackButton.Text = LocalisableString.Interpolate($@"{CommonStrings.Back} ({steps[currentStepIndex.Value - 1].GetLocalisableDescription()})"); + this.FadeIn(); + } - NextButton.Text = isLastStep - ? CommonStrings.Finish - : LocalisableString.Interpolate($@"{CommonStrings.Next} ({steps[currentStepIndex.Value + 1].GetLocalisableDescription()})"); + protected override void PopOut() + { + this.Delay(400).FadeOut(); } } } From e57a0029f16619ed857ed400f5e3695db2f1d3a9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 30 Jun 2024 04:22:59 +0300 Subject: [PATCH 04/15] Remove local footer from `ShearedOverlayContainer` --- .../UserInterface/TestSceneScreenFooter.cs | 2 - osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 - osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 - .../Overlays/Mods/ShearedOverlayContainer.cs | 47 ++----------------- 4 files changed, 3 insertions(+), 50 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs index 70c3664b9a29..de2026e538e4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs @@ -185,8 +185,6 @@ private partial class TestModSelectOverlay : UserModSelectOverlay private partial class TestShearedOverlayContainer : ShearedOverlayContainer { - public override bool UseNewFooter => true; - public TestShearedOverlayContainer() : base(OverlayColourScheme.Orange) { diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index bc11e5d0d219..47f53a3fa659 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -150,8 +150,6 @@ protected override void LoadComplete() private FirstRunSetupFooterContent? currentFooterContent; - public override bool UseNewFooter => true; - public override Drawable CreateFooterContent() => currentFooterContent = new FirstRunSetupFooterContent { ShowNextStep = showNextStep, diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 40be4e08a628..8e18c39cc2d7 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -255,8 +255,6 @@ protected override void LoadComplete() private ModSelectFooterContent? currentFooterContent; - public override bool UseNewFooter => true; - public override Drawable CreateFooterContent() => currentFooterContent = new ModSelectFooterContent(this) { Beatmap = { BindTarget = Beatmap }, diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index b5435e7e5851..1ccf274d0d70 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Footer; @@ -30,17 +29,9 @@ public abstract partial class ShearedOverlayContainer : OsuFocusedOverlayContain /// protected ShearedOverlayHeader Header { get; private set; } = null!; - /// - /// The overlay's footer. - /// - protected Container Footer { get; private set; } - [Resolved] private ScreenFooter? footer { get; set; } - // todo: very temporary property that will be removed once ModSelectOverlay and FirstRunSetupOverlay are updated to use new footer. - public virtual bool UseNewFooter => false; - /// /// A container containing all content, including the header and footer. /// May be used for overlay-wide animations. @@ -52,11 +43,6 @@ public abstract partial class ShearedOverlayContainer : OsuFocusedOverlayContain /// protected Container MainAreaContent { get; private set; } = null!; - /// - /// A container for content that is to be displayed inside the footer. - /// - protected Container FooterContent { get; private set; } - protected override bool StartHidden => true; protected override bool BlockNonPositionalInput => true; @@ -75,8 +61,6 @@ protected ShearedOverlayContainer(OverlayColourScheme colourScheme) [BackgroundDependencyLoader] private void load() { - const float footer_height = ScreenFooter.HEIGHT; - Child = TopLevelContent = new Container { RelativeSizeAxes = Axes.Both, @@ -100,30 +84,9 @@ private void load() Padding = new MarginPadding { Top = ShearedOverlayHeader.HEIGHT, - Bottom = footer_height + PADDING, + Bottom = ScreenFooter.HEIGHT + PADDING, } }, - Footer = new InputBlockingContainer - { - RelativeSizeAxes = Axes.X, - Depth = float.MinValue, - Height = footer_height, - Margin = new MarginPadding { Top = PADDING }, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourProvider.Background5 - }, - FooterContent = new Container - { - RelativeSizeAxes = Axes.Both, - }, - } - } } }; } @@ -160,7 +123,7 @@ protected override void PopIn() Header.MoveToY(0, fade_in_duration, Easing.OutQuint); - if (UseNewFooter && footer != null) + if (footer != null) { footer.SetOverlayContent(this); @@ -170,8 +133,6 @@ protected override void PopIn() hideFooterOnPopOut = true; } } - else - Footer.MoveToY(0, fade_in_duration, Easing.OutQuint); } protected override void PopOut() @@ -183,7 +144,7 @@ protected override void PopOut() Header.MoveToY(-Header.DrawHeight, fade_out_duration, Easing.OutQuint); - if (UseNewFooter && footer != null) + if (footer != null) { footer.ClearOverlayContent(); @@ -193,8 +154,6 @@ protected override void PopOut() hideFooterOnPopOut = false; } } - else - Footer.MoveToY(Footer.DrawHeight, fade_out_duration, Easing.OutQuint); } } } From 58c7d1e7724b6a08b01f4682ccc4684bc311dba0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 29 Jun 2024 10:19:22 +0300 Subject: [PATCH 05/15] Bind game-wide mods bindable to mod select overlay in new song select screen --- osu.Game/Screens/SelectV2/SongSelectV2.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Screens/SelectV2/SongSelectV2.cs b/osu.Game/Screens/SelectV2/SongSelectV2.cs index a8730ad80858..2f9667793fe5 100644 --- a/osu.Game/Screens/SelectV2/SongSelectV2.cs +++ b/osu.Game/Screens/SelectV2/SongSelectV2.cs @@ -64,18 +64,29 @@ protected override void LoadComplete() public override void OnEntering(ScreenTransitionEvent e) { this.FadeIn(); + + modSelectOverlay.SelectedMods.BindTo(Mods); + base.OnEntering(e); } public override void OnResuming(ScreenTransitionEvent e) { this.FadeIn(); + + // required due to https://github.com/ppy/osu-framework/issues/3218 + modSelectOverlay.SelectedMods.Disabled = false; + modSelectOverlay.SelectedMods.BindTo(Mods); + base.OnResuming(e); } public override void OnSuspending(ScreenTransitionEvent e) { this.Delay(400).FadeOut(); + + modSelectOverlay.SelectedMods.UnbindFrom(Mods); + base.OnSuspending(e); } From a65af8249c74e32e512e146943687af33555d155 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 30 Jun 2024 07:27:19 +0300 Subject: [PATCH 06/15] Fix first-run setup buttons reset after reopening from dismiss --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 47f53a3fa659..6412297663f4 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -150,10 +150,16 @@ protected override void LoadComplete() private FirstRunSetupFooterContent? currentFooterContent; - public override Drawable CreateFooterContent() => currentFooterContent = new FirstRunSetupFooterContent + public override Drawable CreateFooterContent() { - ShowNextStep = showNextStep, - }; + currentFooterContent = new FirstRunSetupFooterContent + { + ShowNextStep = showNextStep, + }; + + currentFooterContent.OnLoadComplete += _ => updateButtons(); + return currentFooterContent; + } public override bool OnBackButton() { From 002679ebb0e75ee6d72e9e0e8bfb08a3be4314e9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 10 Jul 2024 11:12:52 +0300 Subject: [PATCH 07/15] Ask for `VisibilityContainer` explicitly --- osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs | 2 +- osu.Game/Overlays/FirstRunSetupOverlay.cs | 2 +- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- osu.Game/Overlays/Mods/ShearedOverlayContainer.cs | 2 +- osu.Game/Screens/Footer/ScreenFooter.cs | 2 +- osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs | 3 ++- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs index de2026e538e4..c8cf6a6ffbee 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs @@ -210,7 +210,7 @@ public override bool OnBackButton() return false; } - public override Drawable CreateFooterContent() => new TestFooterContent(); + public override VisibilityContainer CreateFooterContent() => new TestFooterContent(); public partial class TestFooterContent : VisibilityContainer { diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 6412297663f4..2c8ceba82cfd 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -150,7 +150,7 @@ protected override void LoadComplete() private FirstRunSetupFooterContent? currentFooterContent; - public override Drawable CreateFooterContent() + public override VisibilityContainer CreateFooterContent() { currentFooterContent = new FirstRunSetupFooterContent { diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index bd04a1f6b349..da935396798a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -256,7 +256,7 @@ protected override void LoadComplete() private ModSelectFooterContent? currentFooterContent; - public override Drawable CreateFooterContent() => currentFooterContent = new ModSelectFooterContent(this) + public override VisibilityContainer CreateFooterContent() => currentFooterContent = new ModSelectFooterContent(this) { Beatmap = { BindTarget = Beatmap }, ActiveMods = { BindTarget = ActiveMods }, diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 9e5a336c179d..9ea98c1ae42b 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -104,7 +104,7 @@ private void load() /// /// Creates content to be displayed on the game-wide footer. /// - public virtual Drawable CreateFooterContent() => Empty(); + public virtual VisibilityContainer? CreateFooterContent() => null; /// /// Invoked when the back button in the footer is pressed. diff --git a/osu.Game/Screens/Footer/ScreenFooter.cs b/osu.Game/Screens/Footer/ScreenFooter.cs index a841f2a50b92..4464b9d7dac1 100644 --- a/osu.Game/Screens/Footer/ScreenFooter.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -218,7 +218,7 @@ public void SetActiveOverlayContainer(ShearedOverlayContainer overlay) updateColourScheme(overlay.ColourProvider.ColourScheme); - var content = overlay.CreateFooterContent(); + var content = overlay.CreateFooterContent() ?? Empty(); Add(contentContainer = new Container { diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 0ed45161f24e..2b3ab9491677 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Mods; @@ -34,7 +35,7 @@ public FreeModSelectOverlay() protected override ModColumn CreateModColumn(ModType modType) => new ModColumn(modType, true); - public override Drawable CreateFooterContent() => currentFooterContent = new FreeModSelectFooterContent(this) + public override VisibilityContainer CreateFooterContent() => currentFooterContent = new FreeModSelectFooterContent(this) { Beatmap = { BindTarget = Beatmap }, ActiveMods = { BindTarget = ActiveMods }, From f0a7a3f85657caf09c639bd5cef154d339e5b9de Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 10 Jul 2024 11:51:49 +0300 Subject: [PATCH 08/15] Add failing test case for edge case --- .../UserInterface/TestSceneScreenFooter.cs | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs index c8cf6a6ffbee..a4cf8a276f12 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScreenFooter.cs @@ -23,7 +23,7 @@ public partial class TestSceneScreenFooter : OsuManualInputManagerTestScene { private DependencyProvidingContainer contentContainer = null!; private ScreenFooter screenFooter = null!; - private TestModSelectOverlay overlay = null!; + private TestModSelectOverlay modOverlay = null!; [SetUp] public void SetUp() => Schedule(() => @@ -39,7 +39,7 @@ public void SetUp() => Schedule(() => }, Children = new Drawable[] { - overlay = new TestModSelectOverlay(), + modOverlay = new TestModSelectOverlay(), new PopoverContainer { RelativeSizeAxes = Axes.Both, @@ -51,7 +51,7 @@ public void SetUp() => Schedule(() => screenFooter.SetButtons(new ScreenFooterButton[] { - new ScreenFooterButtonMods(overlay) { Current = SelectedMods }, + new ScreenFooterButtonMods(modOverlay) { Current = SelectedMods }, new ScreenFooterButtonRandom(), new ScreenFooterButtonOptions(), }); @@ -178,6 +178,24 @@ public void TestBackButton() AddAssert("footer hidden", () => screenFooter.State.Value == Visibility.Hidden); } + [Test] + public void TestLoadOverlayAfterFooterIsDisplayed() + { + TestShearedOverlayContainer externalOverlay = null!; + + AddStep("show mod overlay", () => modOverlay.Show()); + AddUntilStep("mod footer content shown", () => this.ChildrenOfType().SingleOrDefault()?.IsPresent, () => Is.True); + + AddStep("add external overlay", () => contentContainer.Add(externalOverlay = new TestShearedOverlayContainer())); + AddUntilStep("wait for load", () => externalOverlay.IsLoaded); + AddAssert("mod footer content still shown", () => this.ChildrenOfType().SingleOrDefault()?.IsPresent, () => Is.True); + AddAssert("external overlay content not shown", () => this.ChildrenOfType().SingleOrDefault()?.IsPresent, () => Is.Not.True); + + AddStep("hide mod overlay", () => modOverlay.Hide()); + AddUntilStep("mod footer content hidden", () => this.ChildrenOfType().SingleOrDefault()?.IsPresent, () => Is.Not.True); + AddAssert("external overlay content still not shown", () => this.ChildrenOfType().SingleOrDefault()?.IsPresent, () => Is.Not.True); + } + private partial class TestModSelectOverlay : UserModSelectOverlay { protected override bool ShowPresets => true; From 337f05f9a454019bed39d6a32f7da94ac4be43fb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 10 Jul 2024 11:54:04 +0300 Subject: [PATCH 09/15] Fix loading (but not showing) a sheared overlay hiding displayed footer content Identified by tests. See https://github.com/ppy/osu/actions/runs/9869382635/job/27253010485 & https://github.com/ppy/osu/actions/runs/9869382635/job/27253009622. This change also prevents the initial `PopOut` call in overlays from calling `clearActiveOverlayContainer`, since it's not in the update thread and it's never meant to be called at that point anyway (it's supposed to be accompanied by a previous `PopIn` call adding the footer content). --- .../Overlays/Mods/ShearedOverlayContainer.cs | 7 +++-- osu.Game/Screens/Footer/ScreenFooter.cs | 26 ++++++++++--------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 9ea98c1ae42b..8c6b9e805b80 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . 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; @@ -123,6 +124,7 @@ protected override bool OnClick(ClickEvent e) return base.OnClick(e); } + private IDisposable? activeOverlayRegistration; private bool hideFooterOnPopOut; protected override void PopIn() @@ -135,7 +137,7 @@ protected override void PopIn() if (footer != null) { - footer.SetActiveOverlayContainer(this); + activeOverlayRegistration = footer.RegisterActiveOverlayContainer(this); if (footer.State.Value == Visibility.Hidden) { @@ -156,7 +158,8 @@ protected override void PopOut() if (footer != null) { - footer.ClearActiveOverlayContainer(); + activeOverlayRegistration?.Dispose(); + activeOverlayRegistration = null; if (hideFooterOnPopOut) { diff --git a/osu.Game/Screens/Footer/ScreenFooter.cs b/osu.Game/Screens/Footer/ScreenFooter.cs index 4464b9d7dac1..f8d222e51068 100644 --- a/osu.Game/Screens/Footer/ScreenFooter.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -142,7 +142,7 @@ public void SetButtons(IReadOnlyList buttons) temporarilyHiddenButtons.Clear(); overlays.Clear(); - ClearActiveOverlayContainer(); + clearActiveOverlayContainer(); var oldButtons = buttonsFlow.ToArray(); @@ -187,14 +187,15 @@ public void SetButtons(IReadOnlyList buttons) private ShearedOverlayContainer? activeOverlay; private Container? contentContainer; + private readonly List temporarilyHiddenButtons = new List(); - public void SetActiveOverlayContainer(ShearedOverlayContainer overlay) + public IDisposable RegisterActiveOverlayContainer(ShearedOverlayContainer overlay) { - if (contentContainer != null) + if (activeOverlay != null) { throw new InvalidOperationException(@"Cannot set overlay content while one is already present. " + - $@"The previous overlay whose content is {contentContainer.Child.GetType().Name} should be hidden first."); + $@"The previous overlay ({activeOverlay.GetType().Name}) should be hidden first."); } activeOverlay = overlay; @@ -232,29 +233,30 @@ public void SetActiveOverlayContainer(ShearedOverlayContainer overlay) this.Delay(60).Schedule(() => content.Show()); else content.Show(); + + return new InvokeOnDisposal(clearActiveOverlayContainer); } - public void ClearActiveOverlayContainer() + private void clearActiveOverlayContainer() { - if (contentContainer == null) + if (activeOverlay == null) return; + Debug.Assert(contentContainer != null); contentContainer.Child.Hide(); double timeUntilRun = contentContainer.Child.LatestTransformEndTime - Time.Current; - Container expireTarget = contentContainer; - contentContainer = null; - activeOverlay = null; - for (int i = 0; i < temporarilyHiddenButtons.Count; i++) makeButtonAppearFromBottom(temporarilyHiddenButtons[i], 0); temporarilyHiddenButtons.Clear(); - expireTarget.Delay(timeUntilRun).Expire(); - updateColourScheme(OverlayColourScheme.Aquamarine); + + contentContainer.Delay(timeUntilRun).Expire(); + contentContainer = null; + activeOverlay = null; } private void updateColourScheme(OverlayColourScheme colourScheme) From 669e945fc334c544274c22b63ebb856ed394b37a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 11 Jul 2024 14:56:17 +0300 Subject: [PATCH 10/15] Fix `ModSelectOverlay` not hiding without a footer --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index da935396798a..4143a7fe8ad0 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -604,7 +604,13 @@ public override bool OnPressed(KeyBindingPressEvent e) return base.OnPressed(e); - void hideOverlay() => footer?.BackButton.TriggerClick(); + void hideOverlay() + { + if (footer != null) + footer.BackButton.TriggerClick(); + else + Hide(); + } } /// From 7a5624fd0efd1c422afc4595b284bf8af444df3e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 11 Jul 2024 15:30:07 +0300 Subject: [PATCH 11/15] Add screen footer to `ScreenTestScene` --- .../Multiplayer/TestSceneMultiplayerMatchSubScreen.cs | 8 ++++---- osu.Game/Tests/Visual/ScreenTestScene.cs | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index f9ef08583838..e2593e68e52d 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -312,14 +312,14 @@ public void TestModSelectOverlay() AddUntilStep("wait for join", () => RoomJoined); ClickButtonWhenEnabled(); - AddAssert("mod select shows unranked", () => screen.UserModsSelectOverlay.ChildrenOfType().Single().Ranked.Value == false); - AddAssert("score multiplier = 1.20", () => screen.UserModsSelectOverlay.ChildrenOfType().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01)); + AddAssert("mod select shows unranked", () => this.ChildrenOfType().Single().Ranked.Value == false); + AddAssert("score multiplier = 1.20", () => this.ChildrenOfType().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01)); AddStep("select flashlight", () => screen.UserModsSelectOverlay.ChildrenOfType().Single(m => m.Mod is ModFlashlight).TriggerClick()); - AddAssert("score multiplier = 1.35", () => screen.UserModsSelectOverlay.ChildrenOfType().Single().ModMultiplier.Value, () => Is.EqualTo(1.35).Within(0.01)); + AddAssert("score multiplier = 1.35", () => this.ChildrenOfType().Single().ModMultiplier.Value, () => Is.EqualTo(1.35).Within(0.01)); AddStep("change flashlight setting", () => ((OsuModFlashlight)screen.UserModsSelectOverlay.SelectedMods.Value.Single()).FollowDelay.Value = 1200); - AddAssert("score multiplier = 1.20", () => screen.UserModsSelectOverlay.ChildrenOfType().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01)); + AddAssert("score multiplier = 1.20", () => this.ChildrenOfType().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01)); } private partial class TestMultiplayerMatchSubScreen : MultiplayerMatchSubScreen diff --git a/osu.Game/Tests/Visual/ScreenTestScene.cs b/osu.Game/Tests/Visual/ScreenTestScene.cs index 3cca1e59cc63..f780b1a8f8e5 100644 --- a/osu.Game/Tests/Visual/ScreenTestScene.cs +++ b/osu.Game/Tests/Visual/ScreenTestScene.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics; using osu.Game.Overlays; using osu.Game.Screens; +using osu.Game.Screens.Footer; namespace osu.Game.Tests.Visual { @@ -30,6 +31,9 @@ public abstract partial class ScreenTestScene : OsuManualInputManagerTestScene, [Cached(typeof(IDialogOverlay))] protected DialogOverlay DialogOverlay { get; private set; } + [Cached] + private ScreenFooter footer; + protected ScreenTestScene() { base.Content.AddRange(new Drawable[] @@ -44,7 +48,8 @@ protected ScreenTestScene() { RelativeSizeAxes = Axes.Both, Child = DialogOverlay = new DialogOverlay() - } + }, + footer = new ScreenFooter(), }); Stack.ScreenPushed += (_, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed → {newScreen}"); From d4a4a059d4a68affe7563d63ede5c9112a919482 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 11 Jul 2024 15:31:02 +0300 Subject: [PATCH 12/15] Fix footer content not accessible by overlay when overriden by a subclass --- osu.Game/Overlays/FirstRunSetupOverlay.cs | 16 ++++++++-------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 8 ++++---- .../Overlays/Mods/ShearedOverlayContainer.cs | 6 +++++- osu.Game/Screens/Footer/ScreenFooter.cs | 6 ++++-- .../Screens/OnlinePlay/FreeModSelectOverlay.cs | 10 +++++----- 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetupOverlay.cs b/osu.Game/Overlays/FirstRunSetupOverlay.cs index 2c8ceba82cfd..1a302cf51d46 100644 --- a/osu.Game/Overlays/FirstRunSetupOverlay.cs +++ b/osu.Game/Overlays/FirstRunSetupOverlay.cs @@ -45,7 +45,7 @@ public partial class FirstRunSetupOverlay : ShearedOverlayContainer private ScreenStack? stack; - public ShearedButton? NextButton => currentFooterContent?.NextButton; + public ShearedButton? NextButton => DisplayedFooterContent?.NextButton; private readonly Bindable showFirstRunSetup = new Bindable(); @@ -148,17 +148,17 @@ protected override void LoadComplete() [Resolved] private ScreenFooter footer { get; set; } = null!; - private FirstRunSetupFooterContent? currentFooterContent; + public new FirstRunSetupFooterContent? DisplayedFooterContent => base.DisplayedFooterContent as FirstRunSetupFooterContent; public override VisibilityContainer CreateFooterContent() { - currentFooterContent = new FirstRunSetupFooterContent + var footerContent = new FirstRunSetupFooterContent { ShowNextStep = showNextStep, }; - currentFooterContent.OnLoadComplete += _ => updateButtons(); - return currentFooterContent; + footerContent.OnLoadComplete += _ => updateButtons(); + return footerContent; } public override bool OnBackButton() @@ -182,7 +182,7 @@ public override bool OnPressed(KeyBindingPressEvent e) switch (e.Action) { case GlobalAction.Select: - currentFooterContent?.NextButton.TriggerClick(); + DisplayedFooterContent?.NextButton.TriggerClick(); return true; case GlobalAction.Back: @@ -287,9 +287,9 @@ private void showNextStep() updateButtons(); } - private void updateButtons() => currentFooterContent?.UpdateButtons(currentStepIndex, steps); + private void updateButtons() => DisplayedFooterContent?.UpdateButtons(currentStepIndex, steps); - private partial class FirstRunSetupFooterContent : VisibilityContainer + public partial class FirstRunSetupFooterContent : VisibilityContainer { public ShearedButton NextButton { get; private set; } = null!; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 4143a7fe8ad0..746959089520 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -254,9 +254,9 @@ protected override void LoadComplete() }); } - private ModSelectFooterContent? currentFooterContent; + public new ModSelectFooterContent? DisplayedFooterContent => base.DisplayedFooterContent as ModSelectFooterContent; - public override VisibilityContainer CreateFooterContent() => currentFooterContent = new ModSelectFooterContent(this) + public override VisibilityContainer CreateFooterContent() => new ModSelectFooterContent(this) { Beatmap = { BindTarget = Beatmap }, ActiveMods = { BindTarget = ActiveMods }, @@ -270,7 +270,7 @@ protected override void Update() base.Update(); SearchTextBox.PlaceholderText = SearchTextBox.HasFocus ? input_search_placeholder : tab_to_search_placeholder; - aboveColumnsContent.Padding = aboveColumnsContent.Padding with { Bottom = currentFooterContent?.DisplaysStackedVertically == true ? 75f : 15f }; + aboveColumnsContent.Padding = aboveColumnsContent.Padding with { Bottom = DisplayedFooterContent?.DisplaysStackedVertically == true ? 75f : 15f }; } /// @@ -573,7 +573,7 @@ public override bool OnPressed(KeyBindingPressEvent e) { if (!SearchTextBox.HasFocus && !customisationPanel.Expanded.Value) { - currentFooterContent?.DeselectAllModsButton?.TriggerClick(); + DisplayedFooterContent?.DeselectAllModsButton?.TriggerClick(); return true; } diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index 8c6b9e805b80..dfa49f377900 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -102,6 +102,8 @@ private void load() }; } + public VisibilityContainer? DisplayedFooterContent { get; private set; } + /// /// Creates content to be displayed on the game-wide footer. /// @@ -137,7 +139,8 @@ protected override void PopIn() if (footer != null) { - activeOverlayRegistration = footer.RegisterActiveOverlayContainer(this); + activeOverlayRegistration = footer.RegisterActiveOverlayContainer(this, out var footerContent); + DisplayedFooterContent = footerContent; if (footer.State.Value == Visibility.Hidden) { @@ -160,6 +163,7 @@ protected override void PopOut() { activeOverlayRegistration?.Dispose(); activeOverlayRegistration = null; + DisplayedFooterContent = null; if (hideFooterOnPopOut) { diff --git a/osu.Game/Screens/Footer/ScreenFooter.cs b/osu.Game/Screens/Footer/ScreenFooter.cs index f8d222e51068..4c020fc95e2b 100644 --- a/osu.Game/Screens/Footer/ScreenFooter.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -190,7 +190,7 @@ public void SetButtons(IReadOnlyList buttons) private readonly List temporarilyHiddenButtons = new List(); - public IDisposable RegisterActiveOverlayContainer(ShearedOverlayContainer overlay) + public IDisposable RegisterActiveOverlayContainer(ShearedOverlayContainer overlay, out VisibilityContainer? footerContent) { if (activeOverlay != null) { @@ -219,7 +219,9 @@ public IDisposable RegisterActiveOverlayContainer(ShearedOverlayContainer overla updateColourScheme(overlay.ColourProvider.ColourScheme); - var content = overlay.CreateFooterContent() ?? Empty(); + footerContent = overlay.CreateFooterContent(); + + var content = footerContent ?? Empty(); Add(contentContainer = new Container { diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 2b3ab9491677..8937abb775ad 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -23,9 +23,7 @@ public partial class FreeModSelectOverlay : ModSelectOverlay set => base.IsValidMod = m => m.UserPlayable && value.Invoke(m); } - private FreeModSelectFooterContent? currentFooterContent; - - protected override SelectAllModsButton? SelectAllModsButton => currentFooterContent?.SelectAllModsButton; + protected override SelectAllModsButton? SelectAllModsButton => DisplayedFooterContent?.SelectAllModsButton; public FreeModSelectOverlay() : base(OverlayColourScheme.Plum) @@ -35,13 +33,15 @@ public FreeModSelectOverlay() protected override ModColumn CreateModColumn(ModType modType) => new ModColumn(modType, true); - public override VisibilityContainer CreateFooterContent() => currentFooterContent = new FreeModSelectFooterContent(this) + public new FreeModSelectFooterContent? DisplayedFooterContent => base.DisplayedFooterContent as FreeModSelectFooterContent; + + public override VisibilityContainer CreateFooterContent() => new FreeModSelectFooterContent(this) { Beatmap = { BindTarget = Beatmap }, ActiveMods = { BindTarget = ActiveMods }, }; - private partial class FreeModSelectFooterContent : ModSelectFooterContent + public partial class FreeModSelectFooterContent : ModSelectFooterContent { private readonly FreeModSelectOverlay overlay; From 3ea0f58daabed92d8e86191379f7d417f9176738 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 11 Jul 2024 15:31:23 +0300 Subject: [PATCH 13/15] Update `TestSceneFreeModSelectOverlay` to work again --- .../TestSceneFreeModSelectOverlay.cs | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs index 938ab1e9f4c2..497faa28d0d6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -16,6 +17,7 @@ using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Footer; using osu.Game.Screens.OnlinePlay; using osu.Game.Utils; using osuTK.Input; @@ -26,6 +28,7 @@ public partial class TestSceneFreeModSelectOverlay : MultiplayerTestScene { private FreeModSelectOverlay freeModSelectOverlay; private FooterButtonFreeMods footerButtonFreeMods; + private ScreenFooter footer; private readonly Bindable>> availableMods = new Bindable>>(); [BackgroundDependencyLoader] @@ -127,7 +130,7 @@ public void TestSelectAllViaFooterButtonThenDeselectFromOverlay() { createFreeModSelect(); - AddAssert("overlay select all button enabled", () => freeModSelectOverlay.ChildrenOfType().Single().Enabled.Value); + AddAssert("overlay select all button enabled", () => this.ChildrenOfType().Single().Enabled.Value); AddAssert("footer button displays off", () => footerButtonFreeMods.ChildrenOfType().Any(t => t.Text == "off")); AddStep("click footer select all button", () => @@ -150,19 +153,27 @@ public void TestSelectAllViaFooterButtonThenDeselectFromOverlay() private void createFreeModSelect() { - AddStep("create free mod select screen", () => Children = new Drawable[] + AddStep("create free mod select screen", () => Child = new DependencyProvidingContainer { - freeModSelectOverlay = new FreeModSelectOverlay + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - State = { Value = Visibility.Visible } - }, - footerButtonFreeMods = new FooterButtonFreeMods(freeModSelectOverlay) - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Current = { BindTarget = freeModSelectOverlay.SelectedMods }, + freeModSelectOverlay = new FreeModSelectOverlay + { + State = { Value = Visibility.Visible } + }, + footerButtonFreeMods = new FooterButtonFreeMods(freeModSelectOverlay) + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Y = -ScreenFooter.HEIGHT, + Current = { BindTarget = freeModSelectOverlay.SelectedMods }, + }, + footer = new ScreenFooter(), }, + CachedDependencies = new (Type, object)[] { (typeof(ScreenFooter), footer) }, }); + AddUntilStep("all column content loaded", () => freeModSelectOverlay.ChildrenOfType().Any() && freeModSelectOverlay.ChildrenOfType().All(column => column.IsLoaded && column.ItemsLoaded)); From e8f7213b3b2907c1c2aee8d0d4be92aeee23f0c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2024 19:20:41 +0900 Subject: [PATCH 14/15] Move logo depth to a forward passing --- osu.Game/OsuGame.cs | 3 +-- osu.Game/Screens/Footer/ScreenFooter.cs | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index dfd9f41e1a37..2580e44e73d9 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -297,8 +297,6 @@ public void CloseAllOverlays(bool hideToolbar = true) if (hideToolbar) Toolbar.Hide(); } - public void ChangeLogoDepth(bool inFrontOfFooter) => ScreenContainer.ChangeChildDepth(logoContainer, inFrontOfFooter ? float.MinValue : 0); - protected override UserInputManager CreateUserInputManager() { var userInputManager = base.CreateUserInputManager(); @@ -996,6 +994,7 @@ protected override void LoadComplete() RelativeSizeAxes = Axes.Both, Child = ScreenFooter = new ScreenFooter(backReceptor) { + RequestLogoInFront = inFront => ScreenContainer.ChangeChildDepth(logoContainer, inFront ? float.MinValue : 0), OnBack = () => { if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen)) diff --git a/osu.Game/Screens/Footer/ScreenFooter.cs b/osu.Game/Screens/Footer/ScreenFooter.cs index 4c020fc95e2b..6a1efcf87a2b 100644 --- a/osu.Game/Screens/Footer/ScreenFooter.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -44,6 +44,8 @@ public partial class ScreenFooter : OverlayContainer public ScreenBackButton BackButton { get; private set; } = null!; + public Action? RequestLogoInFront { get; set; } + public Action? OnBack; public ScreenFooter(BackReceptor? receptor = null) @@ -114,7 +116,7 @@ public void StartTrackingLogo(OsuLogo logo, float duration = 0, Easing easing = changeLogoDepthDelegate = null; logoTrackingContainer.StartTracking(logo, duration, easing); - game?.ChangeLogoDepth(inFrontOfFooter: true); + RequestLogoInFront?.Invoke(true); } public void StopTrackingLogo() @@ -122,7 +124,7 @@ public void StopTrackingLogo() logoTrackingContainer.StopTracking(); if (game != null) - changeLogoDepthDelegate = Scheduler.AddDelayed(() => game.ChangeLogoDepth(inFrontOfFooter: false), transition_duration); + changeLogoDepthDelegate = Scheduler.AddDelayed(() => RequestLogoInFront?.Invoke(false), transition_duration); } protected override void PopIn() From 9a1939a922c29657e60745348469f024c00ff99f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 Jul 2024 21:25:21 +0900 Subject: [PATCH 15/15] Move `logoContainer` local again --- osu.Game/OsuGame.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2580e44e73d9..388a98d94703 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -84,7 +84,7 @@ namespace osu.Game public partial class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo, IPerformFromScreenRunner, IOverlayManager, ILinkHandler { #if DEBUG - // Different port allows runnning release and debug builds alongside each other. + // Different port allows running release and debug builds alongside each other. public const int IPC_PORT = 44824; #else public const int IPC_PORT = 44823; @@ -137,8 +137,6 @@ public partial class OsuGame : OsuGameBase, IKeyBindingHandler, IL protected ScalingContainer ScreenContainer { get; private set; } - private Container logoContainer; - protected Container ScreenOffsetContainer { get; private set; } private Container overlayOffsetContainer; @@ -954,6 +952,8 @@ protected override void LoadComplete() Add(sessionIdleTracker); + Container logoContainer; + AddRange(new Drawable[] { new VolumeControlReceptor