1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-17 00:23:22 +08:00
osu-lazer/osu.Game/Overlays/Mods/ShearedOverlayContainer.cs
Salman Ahmed 467d7c4f54 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).
2024-06-30 06:50:36 +03:00

201 lines
6.9 KiB
C#

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
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;
namespace osu.Game.Overlays.Mods
{
/// <summary>
/// A sheared overlay which provides a header and basic animations.
/// Exposes <see cref="TopLevelContent"/> and <see cref="MainAreaContent"/> as valid targets for content.
/// </summary>
public abstract partial class ShearedOverlayContainer : OsuFocusedOverlayContainer
{
public const float PADDING = 14;
[Cached]
public readonly OverlayColourProvider ColourProvider;
/// <summary>
/// The overlay's header.
/// </summary>
protected ShearedOverlayHeader Header { get; private set; } = null!;
/// <summary>
/// The overlay's footer.
/// </summary>
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;
/// <summary>
/// A container containing all content, including the header and footer.
/// May be used for overlay-wide animations.
/// </summary>
protected Container TopLevelContent { get; private set; } = null!;
/// <summary>
/// A container for content that is to be displayed between the header and footer.
/// </summary>
protected Container MainAreaContent { get; private set; } = null!;
/// <summary>
/// A container for content that is to be displayed inside the footer.
/// </summary>
protected Container FooterContent { get; private set; }
protected override bool StartHidden => true;
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;
ColourProvider = new OverlayColourProvider(colourScheme);
}
[BackgroundDependencyLoader]
private void load()
{
const float footer_height = ScreenFooter.HEIGHT;
Child = TopLevelContent = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourProvider.Background6.Opacity(0.75f),
},
Header = new ShearedOverlayHeader
{
Anchor = Anchor.TopCentre,
Depth = float.MinValue,
Origin = Anchor.TopCentre,
Close = Hide
},
MainAreaContent = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Top = ShearedOverlayHeader.HEIGHT,
Bottom = footer_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,
},
}
}
}
};
}
/// <summary>
/// Creates content to be displayed on the game-wide footer.
/// </summary>
public virtual Drawable CreateFooterContent() => Empty();
/// <summary>
/// Invoked when the back button in the footer is pressed.
/// </summary>
/// <returns>Whether the back button should not close the overlay.</returns>
public virtual bool OnBackButton() => false;
protected override bool OnClick(ClickEvent e)
{
if (State.Value == Visibility.Visible)
{
Hide();
return true;
}
return base.OnClick(e);
}
private bool hideFooterOnPopOut;
protected override void PopIn()
{
const double fade_in_duration = 400;
this.FadeIn(fade_in_duration, Easing.OutQuint);
Header.MoveToY(0, fade_in_duration, Easing.OutQuint);
if (UseNewFooter && footer != null)
{
footer.SetOverlayContent(this);
if (footer.State.Value == Visibility.Hidden)
{
footer.Show();
hideFooterOnPopOut = true;
}
}
else
Footer.MoveToY(0, fade_in_duration, Easing.OutQuint);
}
protected override void PopOut()
{
const double fade_out_duration = 500;
base.PopOut();
this.FadeOut(fade_out_duration, Easing.OutQuint);
Header.MoveToY(-Header.DrawHeight, fade_out_duration, Easing.OutQuint);
if (UseNewFooter && footer != null)
{
footer.ClearOverlayContent();
if (hideFooterOnPopOut)
{
footer.Hide();
hideFooterOnPopOut = false;
}
}
else
Footer.MoveToY(Footer.DrawHeight, fade_out_duration, Easing.OutQuint);
}
}
}