1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-03 01:00:10 +08:00

Merge pull request #28682 from frenzibyte/footer-v2-overlay-content

Add implementation for `ScreenFooter` to house footer content of sheared overlays
This commit is contained in:
Dean Herbert
2024-07-10 15:08:00 +09:00
committed by GitHub
Unverified
8 changed files with 389 additions and 81 deletions
@@ -2,10 +2,16 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Testing;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Screens.Footer;
@@ -15,25 +21,31 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public partial class TestSceneScreenFooter : OsuManualInputManagerTestScene
{
private DependencyProvidingContainer contentContainer = null!;
private ScreenFooter screenFooter = null!;
private TestModSelectOverlay overlay = null!;
[SetUp]
public void SetUp() => Schedule(() =>
{
Children = new Drawable[]
screenFooter = new ScreenFooter();
Child = contentContainer = new DependencyProvidingContainer
{
overlay = new TestModSelectOverlay
RelativeSizeAxes = Axes.Both,
CachedDependencies = new (Type, object)[]
{
Padding = new MarginPadding
{
Bottom = ScreenFooter.HEIGHT
}
(typeof(ScreenFooter), screenFooter)
},
new PopoverContainer
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
Child = screenFooter = new ScreenFooter(),
overlay = new TestModSelectOverlay(),
new PopoverContainer
{
RelativeSizeAxes = Axes.Both,
Depth = float.MinValue,
Child = screenFooter,
},
},
};
@@ -82,14 +94,156 @@ namespace osu.Game.Tests.Visual.UserInterface
}));
}
[Test]
public void TestExternalOverlayContent()
{
TestShearedOverlayContainer externalOverlay = null!;
AddStep("add overlay", () => contentContainer.Add(externalOverlay = new TestShearedOverlayContainer()));
AddStep("set buttons", () => screenFooter.SetButtons(new[]
{
new ScreenFooterButton(externalOverlay)
{
AccentColour = Dependencies.Get<OsuColour>().Orange1,
Icon = FontAwesome.Solid.Toolbox,
Text = "One",
},
new ScreenFooterButton { Text = "Two", Action = () => { } },
new ScreenFooterButton { Text = "Three", Action = () => { } },
}));
AddWaitStep("wait for transition", 3);
AddStep("show overlay", () => externalOverlay.Show());
AddAssert("content displayed in footer", () => screenFooter.ChildrenOfType<TestShearedOverlayContainer.TestFooterContent>().Single().IsPresent);
AddUntilStep("other buttons hidden", () => screenFooter.ChildrenOfType<ScreenFooterButton>().Skip(1).All(b => b.Child.Parent!.Y > 0));
AddStep("hide overlay", () => externalOverlay.Hide());
AddUntilStep("content hidden from footer", () => screenFooter.ChildrenOfType<TestShearedOverlayContainer.TestFooterContent>().SingleOrDefault()?.IsPresent != true);
AddUntilStep("other buttons returned", () => screenFooter.ChildrenOfType<ScreenFooterButton>().Skip(1).All(b => b.ChildrenOfType<Container>().First().Y == 0));
}
[Test]
public void TestTemporarilyShowFooter()
{
TestShearedOverlayContainer externalOverlay = null!;
AddStep("hide footer", () => screenFooter.Hide());
AddStep("remove buttons", () => screenFooter.SetButtons(Array.Empty<ScreenFooterButton>()));
AddStep("add external overlay", () => contentContainer.Add(externalOverlay = new TestShearedOverlayContainer()));
AddStep("show external overlay", () => externalOverlay.Show());
AddAssert("footer shown", () => screenFooter.State.Value == Visibility.Visible);
AddAssert("content displayed in footer", () => screenFooter.ChildrenOfType<TestShearedOverlayContainer.TestFooterContent>().Single().IsPresent);
AddStep("hide external overlay", () => externalOverlay.Hide());
AddAssert("footer hidden", () => screenFooter.State.Value == Visibility.Hidden);
AddUntilStep("content hidden from footer", () => screenFooter.ChildrenOfType<TestShearedOverlayContainer.TestFooterContent>().SingleOrDefault()?.IsPresent != true);
AddStep("show footer", () => screenFooter.Show());
AddAssert("content still hidden from footer", () => screenFooter.ChildrenOfType<TestShearedOverlayContainer.TestFooterContent>().SingleOrDefault()?.IsPresent != true);
AddStep("show external overlay", () => externalOverlay.Show());
AddAssert("footer still visible", () => screenFooter.State.Value == Visibility.Visible);
AddStep("hide external overlay", () => externalOverlay.Hide());
AddAssert("footer still visible", () => screenFooter.State.Value == Visibility.Visible);
AddStep("hide footer", () => screenFooter.Hide());
AddStep("show external overlay", () => externalOverlay.Show());
}
[Test]
public void TestBackButton()
{
TestShearedOverlayContainer externalOverlay = null!;
AddStep("hide footer", () => screenFooter.Hide());
AddStep("remove buttons", () => screenFooter.SetButtons(Array.Empty<ScreenFooterButton>()));
AddStep("add external overlay", () => contentContainer.Add(externalOverlay = new TestShearedOverlayContainer()));
AddStep("show external overlay", () => externalOverlay.Show());
AddAssert("footer shown", () => screenFooter.State.Value == Visibility.Visible);
AddStep("press back", () => this.ChildrenOfType<ScreenBackButton>().Single().TriggerClick());
AddAssert("overlay hidden", () => externalOverlay.State.Value == Visibility.Hidden);
AddAssert("footer hidden", () => screenFooter.State.Value == Visibility.Hidden);
AddStep("show external overlay", () => externalOverlay.Show());
AddStep("set block count", () => externalOverlay.BackButtonCount = 1);
AddStep("press back", () => this.ChildrenOfType<ScreenBackButton>().Single().TriggerClick());
AddAssert("overlay still visible", () => externalOverlay.State.Value == Visibility.Visible);
AddAssert("footer still shown", () => screenFooter.State.Value == Visibility.Visible);
AddStep("press back again", () => this.ChildrenOfType<ScreenBackButton>().Single().TriggerClick());
AddAssert("overlay hidden", () => externalOverlay.State.Value == Visibility.Hidden);
AddAssert("footer hidden", () => screenFooter.State.Value == Visibility.Hidden);
}
private partial class TestModSelectOverlay : UserModSelectOverlay
{
protected override bool ShowPresets => true;
}
public TestModSelectOverlay()
: base(OverlayColourScheme.Aquamarine)
private partial class TestShearedOverlayContainer : ShearedOverlayContainer
{
public override bool UseNewFooter => true;
public TestShearedOverlayContainer()
: base(OverlayColourScheme.Orange)
{
}
[BackgroundDependencyLoader]
private void load()
{
Header.Title = "Test overlay";
Header.Description = "An overlay that is made purely for testing purposes.";
}
public int BackButtonCount;
public override bool OnBackButton()
{
if (BackButtonCount > 0)
{
BackButtonCount--;
return true;
}
return false;
}
public override Drawable CreateFooterContent() => new TestFooterContent();
public partial class TestFooterContent : VisibilityContainer
{
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Children = new[]
{
new ShearedButton(200) { Text = "Action #1", Action = () => { } },
new ShearedButton(140) { Text = "Action #2", Action = () => { } },
}
};
}
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);
}
}
}
}
}
@@ -1,8 +1,6 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -11,45 +9,52 @@ 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 footer and basic animations.
/// Exposes <see cref="TopLevelContent"/>, <see cref="MainAreaContent"/> and <see cref="Footer"/> as valid targets for content.
/// 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
{
protected const float PADDING = 14;
public const float PADDING = 14;
[Cached]
protected readonly OverlayColourProvider ColourProvider;
public readonly OverlayColourProvider ColourProvider;
/// <summary>
/// The overlay's header.
/// </summary>
protected ShearedOverlayHeader Header { get; private set; }
protected ShearedOverlayHeader Header { get; private set; } = null!;
/// <summary>
/// The overlay's footer.
/// </summary>
protected Container Footer { get; private set; }
protected Container Footer { get; private set; } = null!;
[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; }
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; }
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 Container FooterContent { get; private set; } = null!;
protected override bool StartHidden => true;
@@ -65,7 +70,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 +118,17 @@ namespace osu.Game.Overlays.Mods
};
}
/// <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)
@@ -124,6 +140,8 @@ namespace osu.Game.Overlays.Mods
return base.OnClick(e);
}
private bool hideFooterOnPopOut;
protected override void PopIn()
{
const double fade_in_duration = 400;
@@ -131,7 +149,19 @@ 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.SetActiveOverlayContainer(this);
if (footer.State.Value == Visibility.Hidden)
{
footer.Show();
hideFooterOnPopOut = true;
}
}
else
Footer.MoveToY(0, fade_in_duration, Easing.OutQuint);
}
protected override void PopOut()
@@ -142,7 +172,19 @@ 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.ClearActiveOverlayContainer();
if (hideFooterOnPopOut)
{
footer.Hide();
hideFooterOnPopOut = false;
}
}
else
Footer.MoveToY(Footer.DrawHeight, fade_out_duration, Easing.OutQuint);
}
}
}
+13 -3
View File
@@ -9,11 +9,11 @@ namespace osu.Game.Overlays
{
public class OverlayColourProvider
{
private readonly OverlayColourScheme colourScheme;
public OverlayColourScheme ColourScheme { get; private set; }
public OverlayColourProvider(OverlayColourScheme colourScheme)
{
this.colourScheme = colourScheme;
ColourScheme = colourScheme;
}
// Note that the following five colours are also defined in `OsuColour` as `{colourScheme}{0,1,2,3,4}`.
@@ -47,7 +47,17 @@ namespace osu.Game.Overlays
public Color4 Background5 => getColour(0.1f, 0.15f);
public Color4 Background6 => getColour(0.1f, 0.1f);
private Color4 getColour(float saturation, float lightness) => Color4.FromHsl(new Vector4(getBaseHue(colourScheme), saturation, lightness, 1));
/// <summary>
/// Changes the value of <see cref="ColourScheme"/> to a different colour scheme.
/// Note that this does not trigger any kind of signal to any drawable that received colours from here, all drawables need to be updated manually.
/// </summary>
/// <param name="colourScheme">The proposed colour scheme.</param>
public void ChangeColourScheme(OverlayColourScheme colourScheme)
{
ColourScheme = colourScheme;
}
private Color4 getColour(float saturation, float lightness) => Color4.FromHsl(new Vector4(getBaseHue(ColourScheme), saturation, lightness, 1));
// See https://github.com/ppy/osu-web/blob/5a536d217a21582aad999db50a981003d3ad5659/app/helpers.php#L1620-L1628
private static float getBaseHue(OverlayColourScheme colourScheme)
+3 -6
View File
@@ -17,13 +17,10 @@ namespace osu.Game.Screens.Footer
{
public partial class ScreenBackButton : ShearedButton
{
// todo: see https://github.com/ppy/osu-framework/issues/3271
private const float torus_scale_factor = 1.2f;
public const float BUTTON_WIDTH = 240;
public ScreenBackButton()
: base(BUTTON_WIDTH, 70)
: base(BUTTON_WIDTH)
{
}
@@ -42,14 +39,14 @@ namespace osu.Game.Screens.Footer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(20f),
Size = new Vector2(17f),
Icon = FontAwesome.Solid.ChevronLeft,
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.TorusAlternate.With(size: 20 * torus_scale_factor),
Font = OsuFont.TorusAlternate.With(size: 17),
Text = CommonStrings.Back,
UseFullGlyphHeight = false,
}
+117 -18
View File
@@ -14,6 +14,7 @@ using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Screens.Menu;
using osuTK;
@@ -24,11 +25,11 @@ namespace osu.Game.Screens.Footer
private const int padding = 60;
private const float delay_per_button = 30;
public const int HEIGHT = 60;
public const int HEIGHT = 50;
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
private ScreenBackButton backButton = null!;
private Box background = null!;
private FillFlowContainer<ScreenFooterButton> buttonsFlow = null!;
private Container<ScreenFooterButton> removedButtonsContainer = null!;
private LogoTrackingContainer logoTrackingContainer = null!;
@@ -36,6 +37,8 @@ namespace osu.Game.Screens.Footer
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
public ScreenBackButton BackButton { get; private set; } = null!;
public Action? OnBack;
public ScreenFooter(BackReceptor? receptor = null)
@@ -48,7 +51,7 @@ namespace osu.Game.Screens.Footer
if (receptor == null)
Add(receptor = new BackReceptor());
receptor.OnBackPressed = () => backButton.TriggerClick();
receptor.OnBackPressed = () => BackButton.TriggerClick();
}
[BackgroundDependencyLoader]
@@ -56,7 +59,7 @@ namespace osu.Game.Screens.Footer
{
InternalChildren = new Drawable[]
{
new Box
background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background5
@@ -71,12 +74,12 @@ namespace osu.Game.Screens.Footer
Spacing = new Vector2(7, 0),
AutoSizeAxes = Axes.Both
},
backButton = new ScreenBackButton
BackButton = new ScreenBackButton
{
Margin = new MarginPadding { Bottom = 10f, Left = 12f },
Margin = new MarginPadding { Bottom = 15f, Left = 12f },
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Action = () => OnBack?.Invoke(),
Action = onBackPressed,
},
removedButtonsContainer = new Container<ScreenFooterButton>
{
@@ -115,8 +118,11 @@ namespace osu.Game.Screens.Footer
public void SetButtons(IReadOnlyList<ScreenFooterButton> buttons)
{
temporarilyHiddenButtons.Clear();
overlays.Clear();
ClearActiveOverlayContainer();
var oldButtons = buttonsFlow.ToArray();
for (int i = 0; i < oldButtons.Length; i++)
@@ -127,9 +133,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,27 +164,120 @@ namespace osu.Game.Screens.Footer
}
}
private ShearedOverlayContainer? activeOverlay;
private Container? contentContainer;
private readonly List<ScreenFooterButton> temporarilyHiddenButtons = new List<ScreenFooterButton>();
public void SetActiveOverlayContainer(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;
updateColourScheme(overlay.ColourProvider.ColourScheme);
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 ClearActiveOverlayContainer()
{
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();
updateColourScheme(OverlayColourScheme.Aquamarine);
}
private void updateColourScheme(OverlayColourScheme colourScheme)
{
colourProvider.ChangeColourScheme(colourScheme);
background.FadeColour(colourProvider.Background5, 150, Easing.OutQuint);
foreach (var button in buttonsFlow)
button.UpdateDisplay();
}
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)
{
foreach (var o in overlays)
foreach (var o in overlays.Where(o => o != overlay))
o.Hide();
overlay.ToggleVisibility();
}
private void onBackPressed()
{
if (activeOverlay != null)
{
if (o == overlay)
o.ToggleVisibility();
else
o.Hide();
if (activeOverlay.OnBackButton())
return;
activeOverlay.Hide();
return;
}
OnBack?.Invoke();
}
public partial class BackReceptor : Drawable, IKeyBindingHandler<GlobalAction>
+27 -19
View File
@@ -28,8 +28,8 @@ namespace osu.Game.Screens.Footer
private const float shear = OsuGame.SHEAR;
protected const int CORNER_RADIUS = 10;
protected const int BUTTON_HEIGHT = 90;
protected const int BUTTON_WIDTH = 140;
protected const int BUTTON_HEIGHT = 75;
protected const int BUTTON_WIDTH = 116;
public Bindable<Visibility> OverlayState = new Bindable<Visibility>();
@@ -40,7 +40,7 @@ namespace osu.Game.Screens.Footer
private Colour4 buttonAccentColour;
protected Colour4 AccentColour
public Colour4 AccentColour
{
set
{
@@ -50,7 +50,7 @@ namespace osu.Game.Screens.Footer
}
}
protected IconUsage Icon
public IconUsage Icon
{
set => icon.Icon = value;
}
@@ -116,19 +116,18 @@ namespace osu.Game.Screens.Footer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Y = 42,
Y = 35,
AutoSizeAxes = Axes.Both,
Child = text = new OsuSpriteText
{
// figma design says the size is 16, but due to the issues with font sizes 19 matches better
Font = OsuFont.TorusAlternate.With(size: 19),
Font = OsuFont.TorusAlternate.With(size: 16),
AlwaysPresent = true
}
},
icon = new SpriteIcon
{
Y = 12,
Size = new Vector2(20),
Y = 10,
Size = new Vector2(16),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
@@ -140,7 +139,7 @@ namespace osu.Game.Screens.Footer
Anchor = Anchor.BottomCentre,
Origin = Anchor.Centre,
Y = -CORNER_RADIUS,
Size = new Vector2(120, 6),
Size = new Vector2(100, 5),
Masking = true,
CornerRadius = 3,
Child = bar = new Box
@@ -167,12 +166,15 @@ namespace osu.Game.Screens.Footer
if (Overlay != null)
OverlayState.BindTo(Overlay.State);
OverlayState.BindValueChanged(_ => updateDisplay());
Enabled.BindValueChanged(_ => updateDisplay(), true);
OverlayState.BindValueChanged(_ => UpdateDisplay());
Enabled.BindValueChanged(_ => UpdateDisplay(), true);
FinishTransforms(true);
}
// use Content for tracking input as some buttons might be temporarily hidden with DisappearToBottom, and they become hidden by moving Content away from screen.
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Content.ReceivePositionalInputAt(screenSpacePos);
public GlobalAction? Hotkey;
protected override bool OnClick(ClickEvent e)
@@ -187,11 +189,11 @@ namespace osu.Game.Screens.Footer
protected override bool OnHover(HoverEvent e)
{
updateDisplay();
UpdateDisplay();
return true;
}
protected override void OnHoverLost(HoverLostEvent e) => updateDisplay();
protected override void OnHoverLost(HoverLostEvent e) => UpdateDisplay();
public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{
@@ -203,7 +205,7 @@ namespace osu.Game.Screens.Footer
public virtual void OnReleased(KeyBindingReleaseEvent<GlobalAction> e) { }
private void updateDisplay()
public void UpdateDisplay()
{
Color4 backgroundColour = OverlayState.Value == Visibility.Visible ? buttonAccentColour : colourProvider.Background3;
Color4 textColour = OverlayState.Value == Visibility.Visible ? colourProvider.Background6 : colourProvider.Content1;
@@ -228,6 +230,7 @@ namespace osu.Game.Screens.Footer
public void AppearFromLeft(double delay)
{
Content.FinishTransforms();
Content.MoveToX(-300f)
.FadeOut()
.Delay(delay)
@@ -237,6 +240,7 @@ namespace osu.Game.Screens.Footer
public void AppearFromBottom(double delay)
{
Content.FinishTransforms();
Content.MoveToY(100f)
.FadeOut()
.Delay(delay)
@@ -244,22 +248,26 @@ namespace osu.Game.Screens.Footer
.FadeIn(240, Easing.OutCubic);
}
public void DisappearToRightAndExpire(double delay)
public void DisappearToRight(double delay, bool expire)
{
Content.FinishTransforms();
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.FinishTransforms();
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();
}
}
}
@@ -32,9 +32,7 @@ namespace osu.Game.Screens.SelectV2.Footer
{
public partial class ScreenFooterButtonMods : ScreenFooterButton, IHasCurrentValue<IReadOnlyList<Mod>>
{
// todo: see https://github.com/ppy/osu-framework/issues/3271
private const float torus_scale_factor = 1.2f;
private const float bar_height = 37f;
private const float bar_height = 30f;
private const float mod_display_portion = 0.65f;
private readonly BindableWithCurrent<IReadOnlyList<Mod>> current = new BindableWithCurrent<IReadOnlyList<Mod>>(Array.Empty<Mod>());
@@ -112,7 +110,7 @@ namespace osu.Game.Screens.SelectV2.Footer
Origin = Anchor.Centre,
Shear = -BUTTON_SHEAR,
UseFullGlyphHeight = false,
Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold)
Font = OsuFont.Torus.With(size: 14f, weight: FontWeight.Bold)
}
},
new Container
@@ -133,7 +131,7 @@ namespace osu.Game.Screens.SelectV2.Footer
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Shear = -BUTTON_SHEAR,
Scale = new Vector2(0.6f),
Scale = new Vector2(0.5f),
Current = { BindTarget = Current },
ExpansionMode = ExpansionMode.AlwaysContracted,
},
@@ -142,7 +140,7 @@ namespace osu.Game.Screens.SelectV2.Footer
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Shear = -BUTTON_SHEAR,
Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold),
Font = OsuFont.Torus.With(size: 14f, weight: FontWeight.Bold),
Mods = { BindTarget = Current },
}
}
@@ -335,7 +333,7 @@ namespace osu.Game.Screens.SelectV2.Footer
Text = ModSelectOverlayStrings.Unranked.ToUpper(),
Margin = new MarginPadding { Horizontal = 15 },
UseFullGlyphHeight = false,
Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold),
Font = OsuFont.Torus.With(size: 14f, weight: FontWeight.Bold),
Colour = Color4.Black,
}
};
@@ -42,7 +42,7 @@ namespace osu.Game.Screens.SelectV2.Footer
{
randomSpriteText = new OsuSpriteText
{
Font = OsuFont.TorusAlternate.With(size: 19),
Font = OsuFont.TorusAlternate.With(size: 16),
AlwaysPresent = true,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@@ -50,7 +50,7 @@ namespace osu.Game.Screens.SelectV2.Footer
},
rewindSpriteText = new OsuSpriteText
{
Font = OsuFont.TorusAlternate.With(size: 19),
Font = OsuFont.TorusAlternate.With(size: 16),
AlwaysPresent = true,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@@ -75,7 +75,7 @@ namespace osu.Game.Screens.SelectV2.Footer
AlwaysPresent = true, // make sure the button is sized large enough to always show this
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Font = OsuFont.TorusAlternate.With(size: 19),
Font = OsuFont.TorusAlternate.With(size: 16),
});
fallingRewind.FadeOutFromOne(fade_time, Easing.In);