1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 16:32:54 +08:00

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).
This commit is contained in:
Salman Ahmed 2024-06-29 10:17:40 +03:00
parent 900d15e777
commit 467d7c4f54
5 changed files with 82 additions and 53 deletions

View File

@ -181,12 +181,6 @@ namespace osu.Game.Tests.Visual.SongSelect
#endregion #endregion
protected override void Update()
{
base.Update();
Stack.Padding = new MarginPadding { Bottom = screenScreenFooter.DrawHeight - screenScreenFooter.Y };
}
private void updateFooter(IScreen? _, IScreen? newScreen) private void updateFooter(IScreen? _, IScreen? newScreen)
{ {
if (newScreen is IOsuScreen osuScreen && osuScreen.ShowFooter) if (newScreen is IOsuScreen osuScreen && osuScreen.ShowFooter)

View File

@ -51,6 +51,7 @@ using osu.Game.Online.Chat;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapListing; using osu.Game.Overlays.BeatmapListing;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Music; using osu.Game.Overlays.Music;
using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Notifications;
using osu.Game.Overlays.SkinEditor; using osu.Game.Overlays.SkinEditor;
@ -132,8 +133,12 @@ namespace osu.Game
private Container topMostOverlayContent; private Container topMostOverlayContent;
private Container footerBasedOverlayContent;
protected ScalingContainer ScreenContainer { get; private set; } protected ScalingContainer ScreenContainer { get; private set; }
private Container logoContainer;
protected Container ScreenOffsetContainer { get; private set; } protected Container ScreenOffsetContainer { get; private set; }
private Container overlayOffsetContainer; private Container overlayOffsetContainer;
@ -156,8 +161,6 @@ namespace osu.Game
private float toolbarOffset => (Toolbar?.Position.Y ?? 0) + (Toolbar?.DrawHeight ?? 0); private float toolbarOffset => (Toolbar?.Position.Y ?? 0) + (Toolbar?.DrawHeight ?? 0);
private float screenFooterOffset => (ScreenFooter?.DrawHeight ?? 0) - (ScreenFooter?.Position.Y ?? 0);
private IdleTracker idleTracker; private IdleTracker idleTracker;
/// <summary> /// <summary>
@ -242,7 +245,11 @@ namespace osu.Game
throw new ArgumentException($@"{overlayContainer} has already been registered via {nameof(IOverlayManager.RegisterBlockingOverlay)} once."); throw new ArgumentException($@"{overlayContainer} has already been registered via {nameof(IOverlayManager.RegisterBlockingOverlay)} once.");
externalOverlays.Add(overlayContainer); externalOverlays.Add(overlayContainer);
overlayContent.Add(overlayContainer);
if (overlayContainer is ShearedOverlayContainer)
footerBasedOverlayContent.Add(overlayContainer);
else
overlayContent.Add(overlayContainer);
if (overlayContainer is OsuFocusedOverlayContainer focusedOverlayContainer) if (overlayContainer is OsuFocusedOverlayContainer focusedOverlayContainer)
focusedOverlays.Add(focusedOverlayContainer); focusedOverlays.Add(focusedOverlayContainer);
@ -290,6 +297,8 @@ namespace osu.Game
if (hideToolbar) Toolbar.Hide(); if (hideToolbar) Toolbar.Hide();
} }
public void ChangeLogoDepth(bool inFrontOfFooter) => ScreenContainer.ChangeChildDepth(logoContainer, inFrontOfFooter ? float.MinValue : 0);
protected override UserInputManager CreateUserInputManager() protected override UserInputManager CreateUserInputManager()
{ {
var userInputManager = base.CreateUserInputManager(); var userInputManager = base.CreateUserInputManager();
@ -934,7 +943,6 @@ namespace osu.Game
return string.Join(" / ", combinations); return string.Join(" / ", combinations);
}; };
Container logoContainer;
ScreenFooter.BackReceptor backReceptor; ScreenFooter.BackReceptor backReceptor;
dependencies.CacheAs(idleTracker = new GameIdleTracker(6000)); dependencies.CacheAs(idleTracker = new GameIdleTracker(6000));
@ -976,8 +984,15 @@ namespace osu.Game
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Action = () => ScreenFooter.OnBack?.Invoke(), Action = () => ScreenFooter.OnBack?.Invoke(),
}, },
logoContainer = new Container { RelativeSizeAxes = Axes.Both },
footerBasedOverlayContent = new Container
{
Depth = -1,
RelativeSizeAxes = Axes.Both,
},
new PopoverContainer new PopoverContainer
{ {
Depth = -1,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = ScreenFooter = new ScreenFooter(backReceptor) Child = ScreenFooter = new ScreenFooter(backReceptor)
{ {
@ -991,7 +1006,6 @@ namespace osu.Game
} }
}, },
}, },
logoContainer = new Container { RelativeSizeAxes = Axes.Both },
} }
}, },
} }
@ -1025,7 +1039,7 @@ namespace osu.Game
if (!IsDeployedBuild) if (!IsDeployedBuild)
{ {
dependencies.Cache(versionManager = new VersionManager { Depth = int.MinValue }); dependencies.Cache(versionManager = new VersionManager());
loadComponentSingleFile(versionManager, ScreenContainer.Add); loadComponentSingleFile(versionManager, ScreenContainer.Add);
} }
@ -1072,7 +1086,7 @@ namespace osu.Game
loadComponentSingleFile(CreateUpdateManager(), Add, true); loadComponentSingleFile(CreateUpdateManager(), Add, true);
// overlay elements // overlay elements
loadComponentSingleFile(FirstRunOverlay = new FirstRunSetupOverlay(), overlayContent.Add, true); loadComponentSingleFile(FirstRunOverlay = new FirstRunSetupOverlay(), footerBasedOverlayContent.Add, true);
loadComponentSingleFile(new ManageCollectionsDialog(), overlayContent.Add, true); loadComponentSingleFile(new ManageCollectionsDialog(), overlayContent.Add, true);
loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true); loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true);
loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true); loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true);
@ -1137,7 +1151,7 @@ namespace osu.Game
} }
// ensure only one of these overlays are open at once. // 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) foreach (var overlay in singleDisplayOverlays)
{ {
@ -1485,7 +1499,6 @@ namespace osu.Game
ScreenOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset }; ScreenOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset };
overlayOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset }; overlayOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset };
ScreenStack.Padding = new MarginPadding { Bottom = screenFooterOffset };
float horizontalOffset = 0f; float horizontalOffset = 0f;

View File

@ -1,10 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -30,16 +28,15 @@ namespace osu.Game.Overlays.Mods
/// <summary> /// <summary>
/// The overlay's header. /// The overlay's header.
/// </summary> /// </summary>
protected ShearedOverlayHeader Header { get; private set; } protected ShearedOverlayHeader Header { get; private set; } = null!;
/// <summary> /// <summary>
/// The overlay's footer. /// The overlay's footer.
/// </summary> /// </summary>
protected Container Footer { get; private set; } protected Container Footer { get; private set; }
[Resolved(canBeNull: true)] [Resolved]
[CanBeNull] private ScreenFooter? footer { get; set; }
private ScreenFooter footer { get; set; }
// todo: very temporary property that will be removed once ModSelectOverlay and FirstRunSetupOverlay are updated to use new footer. // todo: very temporary property that will be removed once ModSelectOverlay and FirstRunSetupOverlay are updated to use new footer.
public virtual bool UseNewFooter => false; public virtual bool UseNewFooter => false;
@ -48,12 +45,12 @@ namespace osu.Game.Overlays.Mods
/// A container containing all content, including the header and footer. /// A container containing all content, including the header and footer.
/// May be used for overlay-wide animations. /// May be used for overlay-wide animations.
/// </summary> /// </summary>
protected Container TopLevelContent { get; private set; } protected Container TopLevelContent { get; private set; } = null!;
/// <summary> /// <summary>
/// A container for content that is to be displayed between the header and footer. /// A container for content that is to be displayed between the header and footer.
/// </summary> /// </summary>
protected Container MainAreaContent { get; private set; } protected Container MainAreaContent { get; private set; } = null!;
/// <summary> /// <summary>
/// A container for content that is to be displayed inside the footer. /// A container for content that is to be displayed inside the footer.
@ -64,6 +61,10 @@ namespace osu.Game.Overlays.Mods
protected override bool BlockNonPositionalInput => 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) protected ShearedOverlayContainer(OverlayColourScheme colourScheme)
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
@ -81,6 +82,11 @@ namespace osu.Game.Overlays.Mods
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourProvider.Background6.Opacity(0.75f),
},
Header = new ShearedOverlayHeader Header = new ShearedOverlayHeader
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,

View File

@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Threading;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -24,6 +25,7 @@ namespace osu.Game.Screens.Footer
{ {
private const int padding = 60; private const int padding = 60;
private const float delay_per_button = 30; private const float delay_per_button = 30;
private const double transition_duration = 400;
public const int HEIGHT = 50; public const int HEIGHT = 50;
@ -37,6 +39,9 @@ namespace osu.Game.Screens.Footer
[Cached] [Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
[Resolved]
private OsuGame? game { get; set; }
public ScreenBackButton BackButton { get; private set; } = null!; public ScreenBackButton BackButton { get; private set; } = null!;
public Action? OnBack; public Action? OnBack;
@ -101,19 +106,35 @@ namespace osu.Game.Screens.Footer
}; };
} }
public void StartTrackingLogo(OsuLogo logo, float duration = 0, Easing easing = Easing.None) => logoTrackingContainer.StartTracking(logo, duration, easing); private ScheduledDelegate? changeLogoDepthDelegate;
public void StopTrackingLogo() => logoTrackingContainer.StopTracking();
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() protected override void PopIn()
{ {
this.MoveToY(0, 400, Easing.OutQuint) this.MoveToY(0, transition_duration, Easing.OutQuint)
.FadeIn(400, Easing.OutQuint); .FadeIn(transition_duration, Easing.OutQuint);
} }
protected override void PopOut() protected override void PopOut()
{ {
this.MoveToY(HEIGHT, 400, Easing.OutQuint) this.MoveToY(HEIGHT, transition_duration, Easing.OutQuint)
.FadeOut(400, Easing.OutQuint); .FadeOut(transition_duration, Easing.OutQuint);
} }
public void SetButtons(IReadOnlyList<ScreenFooterButton> buttons) public void SetButtons(IReadOnlyList<ScreenFooterButton> buttons)

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Mods; using osu.Game.Overlays.Mods;
@ -23,6 +22,8 @@ namespace osu.Game.Screens.SelectV2
/// </summary> /// </summary>
public partial class SongSelectV2 : OsuScreen public partial class SongSelectV2 : OsuScreen
{ {
private const float logo_scale = 0.4f;
private readonly ModSelectOverlay modSelectOverlay = new SoloModSelectOverlay(); private readonly ModSelectOverlay modSelectOverlay = new SoloModSelectOverlay();
[Cached] [Cached]
@ -30,15 +31,14 @@ namespace osu.Game.Screens.SelectV2
public override bool ShowFooter => true; public override bool ShowFooter => true;
[Resolved]
private OsuLogo? logo { get; set; }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
AddRangeInternal(new Drawable[] AddRangeInternal(new Drawable[]
{ {
new PopoverContainer
{
RelativeSizeAxes = Axes.Both,
},
modSelectOverlay, modSelectOverlay,
}); });
} }
@ -50,6 +50,17 @@ namespace osu.Game.Screens.SelectV2
new ScreenFooterButtonOptions(), 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) public override void OnEntering(ScreenTransitionEvent e)
{ {
this.FadeIn(); this.FadeIn();
@ -74,17 +85,6 @@ namespace osu.Game.Screens.SelectV2
return base.OnExiting(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) protected override void LogoArriving(OsuLogo logo, bool resuming)
{ {
base.LogoArriving(logo, resuming); base.LogoArriving(logo, resuming);
@ -99,7 +99,7 @@ namespace osu.Game.Screens.SelectV2
} }
logo.FadeIn(240, Easing.OutQuint); logo.FadeIn(240, Easing.OutQuint);
logo.ScaleTo(0.4f, 240, Easing.OutQuint); logo.ScaleTo(logo_scale, 240, Easing.OutQuint);
logo.Action = () => logo.Action = () =>
{ {
@ -122,14 +122,9 @@ namespace osu.Game.Screens.SelectV2
logo.FadeOut(120, Easing.Out); logo.FadeOut(120, Easing.Out);
} }
private partial class SoloModSelectOverlay : ModSelectOverlay private partial class SoloModSelectOverlay : UserModSelectOverlay
{ {
protected override bool ShowPresets => true; protected override bool ShowPresets => true;
public SoloModSelectOverlay()
: base(OverlayColourScheme.Aquamarine)
{
}
} }
private partial class PlayerLoaderV2 : PlayerLoader private partial class PlayerLoaderV2 : PlayerLoader