diff --git a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs index acdd1db728..fab2fccb35 100644 --- a/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs +++ b/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs @@ -3,6 +3,7 @@ #nullable disable +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -11,19 +12,20 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Footer; namespace osu.Game.Overlays.Mods { /// - /// A sheared overlay which provides a header and footer and basic animations. - /// Exposes , and as valid targets for content. + /// A sheared overlay which provides a header and basic animations. + /// Exposes and as valid targets for content. /// public abstract partial class ShearedOverlayContainer : OsuFocusedOverlayContainer { - protected const float PADDING = 14; + public const float PADDING = 14; [Cached] - protected readonly OverlayColourProvider ColourProvider; + public readonly OverlayColourProvider ColourProvider; /// /// The overlay's header. @@ -35,6 +37,13 @@ namespace osu.Game.Overlays.Mods /// protected Container Footer { get; private set; } + [Resolved(canBeNull: true)] + [CanBeNull] + 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. @@ -65,7 +74,7 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader] private void load() { - const float footer_height = 50; + const float footer_height = ScreenFooter.HEIGHT; Child = TopLevelContent = new Container { @@ -113,6 +122,11 @@ namespace osu.Game.Overlays.Mods }; } + /// + /// Creates content to be displayed on the game-wide footer. + /// + public virtual Drawable CreateFooterContent() => Empty(); + protected override bool OnClick(ClickEvent e) { if (State.Value == Visibility.Visible) @@ -131,7 +145,13 @@ namespace osu.Game.Overlays.Mods this.FadeIn(fade_in_duration, Easing.OutQuint); Header.MoveToY(0, fade_in_duration, Easing.OutQuint); - Footer.MoveToY(0, fade_in_duration, Easing.OutQuint); + + if (UseNewFooter && footer != null) + { + footer.SetOverlayContent(this); + } + else + Footer.MoveToY(0, fade_in_duration, Easing.OutQuint); } protected override void PopOut() @@ -142,7 +162,13 @@ namespace osu.Game.Overlays.Mods this.FadeOut(fade_out_duration, Easing.OutQuint); Header.MoveToY(-Header.DrawHeight, fade_out_duration, Easing.OutQuint); - Footer.MoveToY(Footer.DrawHeight, fade_out_duration, Easing.OutQuint); + + if (UseNewFooter && footer != null) + { + footer.ClearOverlayContent(); + } + else + Footer.MoveToY(Footer.DrawHeight, fade_out_duration, Easing.OutQuint); } } } diff --git a/osu.Game/Screens/Footer/ScreenFooter.cs b/osu.Game/Screens/Footer/ScreenFooter.cs index c7090ba344..7779b6a4e5 100644 --- a/osu.Game/Screens/Footer/ScreenFooter.cs +++ b/osu.Game/Screens/Footer/ScreenFooter.cs @@ -115,8 +115,11 @@ namespace osu.Game.Screens.Footer public void SetButtons(IReadOnlyList buttons) { + temporarilyHiddenButtons.Clear(); overlays.Clear(); + ClearOverlayContent(); + var oldButtons = buttonsFlow.ToArray(); for (int i = 0; i < oldButtons.Length; i++) @@ -127,9 +130,9 @@ namespace osu.Game.Screens.Footer removedButtonsContainer.Add(oldButton); if (buttons.Count > 0) - makeButtonDisappearToRightAndExpire(oldButton, i, oldButtons.Length); + makeButtonDisappearToRight(oldButton, i, oldButtons.Length, true); else - makeButtonDisappearToBottomAndExpire(oldButton, i, oldButtons.Length); + makeButtonDisappearToBottom(oldButton, i, oldButtons.Length, true); } for (int i = 0; i < buttons.Count; i++) @@ -158,17 +161,85 @@ namespace osu.Game.Screens.Footer } } + private ShearedOverlayContainer? activeOverlay; + private Container? contentContainer; + private readonly List temporarilyHiddenButtons = new List(); + + public void SetOverlayContent(ShearedOverlayContainer overlay) + { + if (contentContainer != 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."); + } + + activeOverlay = overlay; + + Debug.Assert(temporarilyHiddenButtons.Count == 0); + + var targetButton = buttonsFlow.SingleOrDefault(b => b.Overlay == overlay); + + temporarilyHiddenButtons.AddRange(targetButton != null + ? buttonsFlow.SkipWhile(b => b != targetButton).Skip(1) + : buttonsFlow); + + for (int i = 0; i < temporarilyHiddenButtons.Count; i++) + makeButtonDisappearToBottom(temporarilyHiddenButtons[i], 0, 0, false); + + var fallbackPosition = buttonsFlow.Any() + ? buttonsFlow.ToSpaceOfOtherDrawable(Vector2.Zero, this) + : BackButton.ToSpaceOfOtherDrawable(BackButton.LayoutRectangle.TopRight + new Vector2(5f, 0f), this); + + var targetPosition = targetButton?.ToSpaceOfOtherDrawable(targetButton.LayoutRectangle.TopRight, this) ?? fallbackPosition; + + var content = overlay.CreateFooterContent(); + + Add(contentContainer = new Container + { + Y = -15f, + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = targetPosition.X }, + Child = content, + }); + + if (temporarilyHiddenButtons.Count > 0) + this.Delay(60).Schedule(() => content.Show()); + else + content.Show(); + } + + public void ClearOverlayContent() + { + if (contentContainer == null) + return; + + 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(); + } + private void makeButtonAppearFromLeft(ScreenFooterButton button, int index, int count, float startDelay) => button.AppearFromLeft(startDelay + (count - index) * delay_per_button); private void makeButtonAppearFromBottom(ScreenFooterButton button, int index) => button.AppearFromBottom(index * delay_per_button); - private void makeButtonDisappearToRightAndExpire(ScreenFooterButton button, int index, int count) - => button.DisappearToRightAndExpire((count - index) * delay_per_button); + private void makeButtonDisappearToRight(ScreenFooterButton button, int index, int count, bool expire) + => button.DisappearToRight((count - index) * delay_per_button, expire); - private void makeButtonDisappearToBottomAndExpire(ScreenFooterButton button, int index, int count) - => button.DisappearToBottomAndExpire((count - index) * delay_per_button); + private void makeButtonDisappearToBottom(ScreenFooterButton button, int index, int count, bool expire) + => button.DisappearToBottom((count - index) * delay_per_button, expire); private void showOverlay(OverlayContainer overlay) { diff --git a/osu.Game/Screens/Footer/ScreenFooterButton.cs b/osu.Game/Screens/Footer/ScreenFooterButton.cs index cd1de2454b..c1dbbb071d 100644 --- a/osu.Game/Screens/Footer/ScreenFooterButton.cs +++ b/osu.Game/Screens/Footer/ScreenFooterButton.cs @@ -243,22 +243,24 @@ namespace osu.Game.Screens.Footer .FadeIn(240, Easing.OutCubic); } - public void DisappearToRightAndExpire(double delay) + public void DisappearToRight(double delay, bool expire) { Content.Delay(delay) .FadeOut(240, Easing.InOutCubic) .MoveToX(300f, 360, Easing.InOutCubic); - this.Delay(Content.LatestTransformEndTime - Time.Current).Expire(); + if (expire) + this.Delay(Content.LatestTransformEndTime - Time.Current).Expire(); } - public void DisappearToBottomAndExpire(double delay) + public void DisappearToBottom(double delay, bool expire) { Content.Delay(delay) .FadeOut(240, Easing.InOutCubic) .MoveToY(100f, 240, Easing.InOutCubic); - this.Delay(Content.LatestTransformEndTime - Time.Current).Expire(); + if (expire) + this.Delay(Content.LatestTransformEndTime - Time.Current).Expire(); } } }