1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-15 01:57:51 +08:00

Various renaming and class updates to allow multiple menu banners

This commit is contained in:
Dean Herbert 2024-03-23 16:41:40 +08:00
parent 77660e57ea
commit ef2a16dd8f
No known key found for this signature in database
7 changed files with 169 additions and 80 deletions

View File

@ -13,30 +13,48 @@ namespace osu.Game.Tests.Visual.Menus
{ {
public partial class TestSceneMainMenu : OsuGameTestScene public partial class TestSceneMainMenu : OsuGameTestScene
{ {
private SystemTitle systemTitle => Game.ChildrenOfType<SystemTitle>().Single(); private OnlineMenuBanner onlineMenuBanner => Game.ChildrenOfType<OnlineMenuBanner>().Single();
[Test] [Test]
public void TestSystemTitle() public void TestOnlineMenuBanner()
{ {
AddStep("set system title", () => systemTitle.Current.Value = new APISystemTitle AddStep("set online content", () => onlineMenuBanner.Current.Value = new APIMenuContent
{ {
Image = @"https://assets.ppy.sh/main-menu/project-loved-2@2x.png", Images = new[]
Url = @"https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023", {
new APIMenuImage
{
Image = @"https://assets.ppy.sh/main-menu/project-loved-2@2x.png",
Url = @"https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023",
}
}
}); });
AddAssert("system title not visible", () => systemTitle.State.Value, () => Is.EqualTo(Visibility.Hidden)); AddAssert("system title not visible", () => onlineMenuBanner.State.Value, () => Is.EqualTo(Visibility.Hidden));
AddStep("enter menu", () => InputManager.Key(Key.Enter)); AddStep("enter menu", () => InputManager.Key(Key.Enter));
AddUntilStep("system title visible", () => systemTitle.State.Value, () => Is.EqualTo(Visibility.Visible)); AddUntilStep("system title visible", () => onlineMenuBanner.State.Value, () => Is.EqualTo(Visibility.Visible));
AddStep("set another title", () => systemTitle.Current.Value = new APISystemTitle AddStep("set another title", () => onlineMenuBanner.Current.Value = new APIMenuContent
{ {
Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png", Images = new[]
Url = @"https://osu.ppy.sh/community/contests/189", {
new APIMenuImage
{
Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png",
Url = @"https://osu.ppy.sh/community/contests/189",
}
}
}); });
AddStep("set title with nonexistent image", () => systemTitle.Current.Value = new APISystemTitle AddStep("set title with nonexistent image", () => onlineMenuBanner.Current.Value = new APIMenuContent
{ {
Image = @"https://test.invalid/@2x", // .invalid TLD reserved by https://datatracker.ietf.org/doc/html/rfc2606#section-2 Images = new[]
Url = @"https://osu.ppy.sh/community/contests/189", {
new APIMenuImage
{
Image = @"https://test.invalid/@2x", // .invalid TLD reserved by https://datatracker.ietf.org/doc/html/rfc2606#section-2
Url = @"https://osu.ppy.sh/community/contests/189",
}
}
}); });
AddStep("unset system title", () => systemTitle.Current.Value = null); AddStep("unset system title", () => onlineMenuBanner.Current.Value = new APIMenuContent());
} }
} }
} }

View File

@ -5,9 +5,9 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class GetSystemTitleRequest : OsuJsonWebRequest<APISystemTitle> public class GetMenuContentRequest : OsuJsonWebRequest<APIMenuContent>
{ {
public GetSystemTitleRequest() public GetMenuContentRequest()
: base(@"https://assets.ppy.sh/lazer-status.json") : base(@"https://assets.ppy.sh/lazer-status.json")
{ {
} }

View File

@ -0,0 +1,42 @@
// 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 System;
using Newtonsoft.Json;
namespace osu.Game.Online.API.Requests.Responses
{
public class APIMenuContent : IEquatable<APIMenuContent>
{
/// <summary>
/// Images which should be displayed in rotation.
/// </summary>
[JsonProperty(@"images")]
public APIMenuImage[] Images { get; init; } = Array.Empty<APIMenuImage>();
public DateTimeOffset LastUpdated { get; init; }
public bool Equals(APIMenuContent? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return LastUpdated.Equals(other.LastUpdated);
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((APIMenuContent)obj);
}
public override int GetHashCode()
{
return LastUpdated.GetHashCode();
}
}
}

View File

@ -0,0 +1,57 @@
// 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 System;
using Newtonsoft.Json;
namespace osu.Game.Online.API.Requests.Responses
{
public class APIMenuImage : IEquatable<APIMenuImage>
{
/// <summary>
/// A URL pointing to the image which should be displayed. Generally should be an @2x image filename.
/// </summary>
[JsonProperty(@"image")]
public string Image { get; init; } = string.Empty;
/// <summary>
/// A URL that should be opened on clicking the image.
/// </summary>
[JsonProperty(@"url")]
public string Url { get; init; } = string.Empty;
/// <summary>
/// The time at which this item should begin displaying. If <c>null</c>, will display immediately.
/// </summary>
[JsonProperty(@"begins")]
public DateTimeOffset? Begins { get; set; }
/// <summary>
/// The time at which this item should stop displaying. If <c>null</c>, will display indefinitely.
/// </summary>
[JsonProperty(@"expires")]
public DateTimeOffset? Expires { get; set; }
public bool Equals(APIMenuImage? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Image == other.Image && Url == other.Url;
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((APIMenuImage)obj);
}
public override int GetHashCode()
{
return HashCode.Combine(Image, Url);
}
}
}

View File

@ -1,30 +0,0 @@
// 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 System;
using Newtonsoft.Json;
namespace osu.Game.Online.API.Requests.Responses
{
public class APISystemTitle : IEquatable<APISystemTitle>
{
[JsonProperty(@"image")]
public string Image { get; set; } = string.Empty;
[JsonProperty(@"url")]
public string Url { get; set; } = string.Empty;
public bool Equals(APISystemTitle? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Image == other.Image && Url == other.Url;
}
public override bool Equals(object? obj) => obj is APISystemTitle other && Equals(other);
// ReSharper disable NonReadonlyMemberInGetHashCode
public override int GetHashCode() => HashCode.Combine(Image, Url);
}
}

View File

@ -98,7 +98,7 @@ namespace osu.Game.Screens.Menu
private ParallaxContainer buttonsContainer; private ParallaxContainer buttonsContainer;
private SongTicker songTicker; private SongTicker songTicker;
private Container logoTarget; private Container logoTarget;
private SystemTitle systemTitle; private OnlineMenuBanner onlineMenuBanner;
private MenuTip menuTip; private MenuTip menuTip;
private FillFlowContainer bottomElementsFlow; private FillFlowContainer bottomElementsFlow;
private SupporterDisplay supporterDisplay; private SupporterDisplay supporterDisplay;
@ -178,7 +178,7 @@ namespace osu.Game.Screens.Menu
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
}, },
systemTitle = new SystemTitle onlineMenuBanner = new OnlineMenuBanner
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
@ -201,12 +201,12 @@ namespace osu.Game.Screens.Menu
case ButtonSystemState.Initial: case ButtonSystemState.Initial:
case ButtonSystemState.Exit: case ButtonSystemState.Exit:
ApplyToBackground(b => b.FadeColour(Color4.White, 500, Easing.OutSine)); ApplyToBackground(b => b.FadeColour(Color4.White, 500, Easing.OutSine));
systemTitle.State.Value = Visibility.Hidden; onlineMenuBanner.State.Value = Visibility.Hidden;
break; break;
default: default:
ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine)); ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine));
systemTitle.State.Value = Visibility.Visible; onlineMenuBanner.State.Value = Visibility.Visible;
break; break;
} }
}; };

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -18,42 +19,28 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Screens.Menu namespace osu.Game.Screens.Menu
{ {
public partial class SystemTitle : VisibilityContainer public partial class OnlineMenuBanner : VisibilityContainer
{ {
internal Bindable<APISystemTitle?> Current { get; } = new Bindable<APISystemTitle?>(); internal Bindable<APIMenuContent> Current { get; } = new Bindable<APIMenuContent>(new APIMenuContent());
private const float transition_duration = 500; private const float transition_duration = 500;
private Container content = null!; private Container content = null!;
private CancellationTokenSource? cancellationTokenSource; private CancellationTokenSource? cancellationTokenSource;
private SystemTitleImage? currentImage; private MenuImage? currentImage;
private ScheduledDelegate? openUrlAction;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuGame? game) private void load()
{ {
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
AutoSizeDuration = transition_duration; AutoSizeDuration = transition_duration;
AutoSizeEasing = Easing.OutQuint; AutoSizeEasing = Easing.OutQuint;
InternalChild = content = new OsuClickableContainer InternalChild = content = new Container
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Action = () =>
{
currentImage?.Flash();
// Delay slightly to allow animation to play out.
openUrlAction?.Cancel();
openUrlAction = Scheduler.AddDelayed(() =>
{
if (!string.IsNullOrEmpty(Current.Value?.Url))
game?.HandleLink(Current.Value.Url);
}, 250);
}
}; };
} }
@ -98,7 +85,7 @@ namespace osu.Game.Screens.Menu
private void checkForUpdates() private void checkForUpdates()
{ {
var request = new GetSystemTitleRequest(); var request = new GetMenuContentRequest();
Task.Run(() => request.Perform()) Task.Run(() => request.Perform())
.ContinueWith(r => .ContinueWith(r =>
{ {
@ -121,12 +108,12 @@ namespace osu.Game.Screens.Menu
cancellationTokenSource = null; cancellationTokenSource = null;
currentImage?.FadeOut(500, Easing.OutQuint).Expire(); currentImage?.FadeOut(500, Easing.OutQuint).Expire();
if (string.IsNullOrEmpty(Current.Value?.Image)) if (Current.Value.Images.Length == 0)
return; return;
LoadComponentAsync(new SystemTitleImage(Current.Value), loaded => LoadComponentAsync(new MenuImage(Current.Value.Images.First()), loaded =>
{ {
if (!loaded.SystemTitle.Equals(Current.Value)) if (!loaded.Image.Equals(Current.Value.Images.First()))
loaded.Dispose(); loaded.Dispose();
content.Add(currentImage = loaded); content.Add(currentImage = loaded);
@ -134,22 +121,24 @@ namespace osu.Game.Screens.Menu
} }
[LongRunningLoad] [LongRunningLoad]
private partial class SystemTitleImage : CompositeDrawable private partial class MenuImage : OsuClickableContainer
{ {
public readonly APISystemTitle SystemTitle; public readonly APIMenuImage Image;
private Sprite flash = null!; private Sprite flash = null!;
public SystemTitleImage(APISystemTitle systemTitle) private ScheduledDelegate? openUrlAction;
public MenuImage(APIMenuImage image)
{ {
SystemTitle = systemTitle; Image = image;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(LargeTextureStore textureStore) private void load(LargeTextureStore textureStore, OsuGame game)
{ {
Texture? texture = textureStore.Get(SystemTitle.Image); Texture? texture = textureStore.Get(Image.Image);
if (texture != null && SystemTitle.Image.Contains(@"@2x")) if (texture != null && Image.Image.Contains(@"@2x"))
texture.ScaleAdjust *= 2; texture.ScaleAdjust *= 2;
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
@ -163,6 +152,19 @@ namespace osu.Game.Screens.Menu
Blending = BlendingParameters.Additive, Blending = BlendingParameters.Additive,
}, },
}; };
Action = () =>
{
Flash();
// Delay slightly to allow animation to play out.
openUrlAction?.Cancel();
openUrlAction = Scheduler.AddDelayed(() =>
{
if (!string.IsNullOrEmpty(Image.Url))
game?.HandleLink(Image.Url);
}, 250);
};
} }
protected override void LoadComplete() protected override void LoadComplete()