Merge pull request #17862 from peppy/i-dialog-overlay

Split out `IDialogOverlay` to allow for easier testing
This commit is contained in:
Dan Balasescu 2022-04-19 05:24:05 +09:00 committed by GitHub
commit 94e892df1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 78 additions and 40 deletions

View File

@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Collections
}); });
Dependencies.Cache(manager); Dependencies.Cache(manager);
Dependencies.Cache(dialogOverlay); Dependencies.CacheAs<IDialogOverlay>(dialogOverlay);
} }
[SetUp] [SetUp]

View File

@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Navigation
typeof(LoginOverlay), typeof(LoginOverlay),
typeof(MusicController), typeof(MusicController),
typeof(AccountCreationOverlay), typeof(AccountCreationOverlay),
typeof(DialogOverlay), typeof(IDialogOverlay),
typeof(ScreenshotManager) typeof(ScreenshotManager)
}; };

View File

@ -5,6 +5,7 @@ using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing; using osu.Framework.Testing;
@ -113,12 +114,12 @@ namespace osu.Game.Tests.Visual.Navigation
AddAssert("did not perform", () => !actionPerformed); AddAssert("did not perform", () => !actionPerformed);
AddAssert("only one exit attempt", () => blocker.ExitAttempts == 1); AddAssert("only one exit attempt", () => blocker.ExitAttempts == 1);
AddUntilStep("wait for dialog display", () => Game.Dependencies.Get<DialogOverlay>().IsLoaded); waitForDialogOverlayLoad();
if (confirmed) if (confirmed)
{ {
AddStep("accept dialog", () => InputManager.Key(Key.Number1)); AddStep("accept dialog", () => InputManager.Key(Key.Number1));
AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<DialogOverlay>().CurrentDialog == null); AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog == null);
AddUntilStep("did perform", () => actionPerformed); AddUntilStep("did perform", () => actionPerformed);
} }
else else
@ -145,7 +146,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddWaitStep("wait a bit", 10); AddWaitStep("wait a bit", 10);
AddUntilStep("wait for dialog display", () => Game.Dependencies.Get<DialogOverlay>().IsLoaded); waitForDialogOverlayLoad();
AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == blocker2); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == blocker2);
AddAssert("did not perform", () => !actionPerformed); AddAssert("did not perform", () => !actionPerformed);
@ -187,7 +188,7 @@ namespace osu.Game.Tests.Visual.Navigation
AddWaitStep("wait a bit", 10); AddWaitStep("wait a bit", 10);
AddUntilStep("wait for dialog display", () => Game.Dependencies.Get<DialogOverlay>().IsLoaded); waitForDialogOverlayLoad();
AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack);
AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker);
@ -211,6 +212,8 @@ namespace osu.Game.Tests.Visual.Navigation
} }
} }
private void waitForDialogOverlayLoad() => AddUntilStep("wait for dialog overlay loaded", () => ((Drawable)Game.Dependencies.Get<IDialogOverlay>()).IsLoaded);
private void importAndWaitForSongSelect() private void importAndWaitForSongSelect()
{ {
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
@ -221,7 +224,7 @@ namespace osu.Game.Tests.Visual.Navigation
public class DialogBlockingScreen : OsuScreen public class DialogBlockingScreen : OsuScreen
{ {
[Resolved] [Resolved]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
private int dialogDisplayCount; private int dialogDisplayCount;

View File

@ -6,6 +6,7 @@ using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Screens; using osu.Framework.Screens;
@ -200,10 +201,10 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("choose clear all scores", () => InputManager.Key(Key.Number4)); AddStep("choose clear all scores", () => InputManager.Key(Key.Number4));
AddUntilStep("wait for dialog display", () => Game.Dependencies.Get<DialogOverlay>().IsLoaded); AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get<IDialogOverlay>()).IsLoaded);
AddUntilStep("wait for dialog", () => Game.Dependencies.Get<DialogOverlay>().CurrentDialog != null); AddUntilStep("wait for dialog", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog != null);
AddStep("confirm deletion", () => InputManager.Key(Key.Number1)); AddStep("confirm deletion", () => InputManager.Key(Key.Number1));
AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<DialogOverlay>().CurrentDialog == null); AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog == null);
AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find<ScoreInfo>(score.ID)?.DeletePending == true)); AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find<ScoreInfo>(score.ID)?.DeletePending == true));
@ -246,10 +247,10 @@ namespace osu.Game.Tests.Visual.Navigation
InputManager.Click(MouseButton.Left); InputManager.Click(MouseButton.Left);
}); });
AddUntilStep("wait for dialog display", () => Game.Dependencies.Get<DialogOverlay>().IsLoaded); AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get<IDialogOverlay>()).IsLoaded);
AddUntilStep("wait for dialog", () => Game.Dependencies.Get<DialogOverlay>().CurrentDialog != null); AddUntilStep("wait for dialog", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog != null);
AddStep("confirm deletion", () => InputManager.Key(Key.Number1)); AddStep("confirm deletion", () => InputManager.Key(Key.Number1));
AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<DialogOverlay>().CurrentDialog == null); AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog == null);
AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find<ScoreInfo>(score.ID)?.DeletePending == true)); AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find<ScoreInfo>(score.ID)?.DeletePending == true));

View File

@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online
Dependencies.Cache(chatManager); Dependencies.Cache(chatManager);
Dependencies.Cache(new ChatOverlay()); Dependencies.Cache(new ChatOverlay());
Dependencies.Cache(dialogOverlay); Dependencies.CacheAs<IDialogOverlay>(dialogOverlay);
} }
[SetUp] [SetUp]

View File

@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Settings
Depth = -1 Depth = -1
}); });
Dependencies.Cache(dialogOverlay); Dependencies.CacheAs<IDialogOverlay>(dialogOverlay);
} }
} }
} }

View File

@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
private readonly FailableLeaderboard leaderboard; private readonly FailableLeaderboard leaderboard;
[Cached] [Cached(typeof(IDialogOverlay))]
private readonly DialogOverlay dialogOverlay; private readonly DialogOverlay dialogOverlay;
private ScoreManager scoreManager; private ScoreManager scoreManager;

View File

@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
public class TestSceneUserTopScoreContainer : OsuTestScene public class TestSceneUserTopScoreContainer : OsuTestScene
{ {
[Cached] [Cached(typeof(IDialogOverlay))]
private readonly DialogOverlay dialogOverlay; private readonly DialogOverlay dialogOverlay;
public TestSceneUserTopScoreContainer() public TestSceneUserTopScoreContainer()

View File

@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private BeatmapInfo beatmapInfo; private BeatmapInfo beatmapInfo;
[Cached] [Cached(typeof(IDialogOverlay))]
private readonly DialogOverlay dialogOverlay; private readonly DialogOverlay dialogOverlay;
public TestSceneDeleteLocalScore() public TestSceneDeleteLocalScore()

View File

@ -158,7 +158,7 @@ namespace osu.Game.Collections
public Func<Vector2, bool> IsTextBoxHovered; public Func<Vector2, bool> IsTextBoxHovered;
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private CollectionManager collectionManager { get; set; } private CollectionManager collectionManager { get; set; }

View File

@ -40,7 +40,7 @@ namespace osu.Game.Database
private OsuGame game { get; set; } private OsuGame game { get; set; }
[Resolved] [Resolved]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private DesktopGameHost desktopGameHost { get; set; } private DesktopGameHost desktopGameHost { get; set; }

View File

@ -17,7 +17,7 @@ namespace osu.Game.Online.Chat
private GameHost host { get; set; } private GameHost host { get; set; }
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
private Bindable<bool> externalLinkWarning; private Bindable<bool> externalLinkWarning;

View File

@ -64,7 +64,7 @@ namespace osu.Game.Online.Leaderboards
private List<ScoreComponentLabel> statisticsLabels; private List<ScoreComponentLabel> statisticsLabels;
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private SongSelect songSelect { get; set; } private SongSelect songSelect { get; set; }

View File

@ -819,7 +819,7 @@ namespace osu.Game
}, rightFloatingOverlayContent.Add, true); }, rightFloatingOverlayContent.Add, true);
loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true);
loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile<IDialogOverlay>(new DialogOverlay(), topMostOverlayContent.Add, true);
loadComponentSingleFile(CreateHighPerformanceSession(), Add); loadComponentSingleFile(CreateHighPerformanceSession(), Add);
@ -976,12 +976,14 @@ namespace osu.Game
/// <param name="component">The component to load.</param> /// <param name="component">The component to load.</param>
/// <param name="loadCompleteAction">An action to invoke on load completion (generally to add the component to the hierarchy).</param> /// <param name="loadCompleteAction">An action to invoke on load completion (generally to add the component to the hierarchy).</param>
/// <param name="cache">Whether to cache the component as type <typeparamref name="T"/> into the game dependencies before any scheduling.</param> /// <param name="cache">Whether to cache the component as type <typeparamref name="T"/> into the game dependencies before any scheduling.</param>
private T loadComponentSingleFile<T>(T component, Action<T> loadCompleteAction, bool cache = false) private T loadComponentSingleFile<T>(T component, Action<Drawable> loadCompleteAction, bool cache = false)
where T : Drawable where T : class
{ {
if (cache) if (cache)
dependencies.CacheAs(component); dependencies.CacheAs(component);
var drawableComponent = component as Drawable ?? throw new ArgumentException($"Component must be a {nameof(Drawable)}", nameof(component));
if (component is OsuFocusedOverlayContainer overlay) if (component is OsuFocusedOverlayContainer overlay)
focusedOverlays.Add(overlay); focusedOverlays.Add(overlay);
@ -1005,7 +1007,7 @@ namespace osu.Game
// Since this is running in a separate thread, it is possible for OsuGame to be disposed after LoadComponentAsync has been called // Since this is running in a separate thread, it is possible for OsuGame to be disposed after LoadComponentAsync has been called
// throwing an exception. To avoid this, the call is scheduled on the update thread, which does not run if IsDisposed = true // throwing an exception. To avoid this, the call is scheduled on the update thread, which does not run if IsDisposed = true
Task task = null; Task task = null;
var del = new ScheduledDelegate(() => task = LoadComponentAsync(component, loadCompleteAction)); var del = new ScheduledDelegate(() => task = LoadComponentAsync(drawableComponent, loadCompleteAction));
Scheduler.Add(del); Scheduler.Add(del);
// The delegate won't complete if OsuGame has been disposed in the meantime // The delegate won't complete if OsuGame has been disposed in the meantime

View File

@ -216,7 +216,7 @@ namespace osu.Game.Overlays.Dialog
}; };
// It's important we start in a visible state so our state fires on hide, even before load. // It's important we start in a visible state so our state fires on hide, even before load.
// This is used by the DialogOverlay to know when the dialog was dismissed. // This is used by the dialog overlay to know when the dialog was dismissed.
Show(); Show();
} }

View File

@ -14,7 +14,7 @@ using osu.Game.Audio.Effects;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
public class DialogOverlay : OsuFocusedOverlayContainer public class DialogOverlay : OsuFocusedOverlayContainer, IDialogOverlay
{ {
private readonly Container dialogContainer; private readonly Container dialogContainer;

View File

@ -0,0 +1,32 @@
// 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 enable
using osu.Framework.Allocation;
using osu.Game.Overlays.Dialog;
namespace osu.Game.Overlays
{
/// <summary>
/// A global overlay that can show popup dialogs.
/// </summary>
[Cached(typeof(IDialogOverlay))]
public interface IDialogOverlay
{
/// <summary>
/// Push a new dialog for display.
/// </summary>
/// <remarks>
/// This will immediate dismiss any already displayed dialog (cancelling the action).
/// If the dialog instance provided is already displayed, it will be a noop.
/// </remarks>
/// <param name="dialog">The dialog to be presented.</param>
void Push(PopupDialog dialog);
/// <summary>
/// The currently displayed dialog, if any.
/// </summary>
PopupDialog? CurrentDialog { get; }
}
}

View File

@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
private SettingsButton undeleteButton; private SettingsButton undeleteButton;
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] LegacyImportManager legacyImportManager, DialogOverlay dialogOverlay) private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] LegacyImportManager legacyImportManager, IDialogOverlay dialogOverlay)
{ {
if (legacyImportManager?.SupportsImportFromStable == true) if (legacyImportManager?.SupportsImportFromStable == true)
{ {

View File

@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
private OsuGameBase game { get; set; } private OsuGameBase game { get; set; }
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
protected override DirectoryInfo InitialPath => new DirectoryInfo(storage.GetFullPath(string.Empty)).Parent; protected override DirectoryInfo InitialPath => new DirectoryInfo(storage.GetFullPath(string.Empty)).Parent;

View File

@ -26,7 +26,7 @@ namespace osu.Game
private NotificationOverlay notifications { get; set; } private NotificationOverlay notifications { get; set; }
[Resolved] [Resolved]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private OsuGame game { get; set; } private OsuGame game { get; set; }

View File

@ -84,7 +84,7 @@ namespace osu.Game.Screens.Edit
private Storage storage { get; set; } private Storage storage { get; set; }
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private NotificationOverlay notifications { get; set; } private NotificationOverlay notifications { get; set; }

View File

@ -60,7 +60,7 @@ namespace osu.Game.Screens.Menu
private IAPIProvider api { get; set; } private IAPIProvider api { get; set; }
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
private BackgroundScreenDefault background; private BackgroundScreenDefault background;

View File

@ -13,7 +13,7 @@ namespace osu.Game.Screens.Menu
public class StorageErrorDialog : PopupDialog public class StorageErrorDialog : PopupDialog
{ {
[Resolved] [Resolved]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
public StorageErrorDialog(OsuStorage storage, OsuStorageError error) public StorageErrorDialog(OsuStorage storage, OsuStorageError error)
{ {

View File

@ -238,7 +238,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
} }
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
private bool exitConfirmed; private bool exitConfirmed;

View File

@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select.Carousel
private Action<int> viewDetails; private Action<int> viewDetails;
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private CollectionManager collectionManager { get; set; } private CollectionManager collectionManager { get; set; }

View File

@ -87,7 +87,7 @@ namespace osu.Game.Screens.Select
protected Container LeftArea { get; private set; } protected Container LeftArea { get; private set; }
private BeatmapInfoWedge beatmapInfoWedge; private BeatmapInfoWedge beatmapInfoWedge;
private DialogOverlay dialogOverlay; private IDialogOverlay dialogOverlay;
[Resolved] [Resolved]
private BeatmapManager beatmaps { get; set; } private BeatmapManager beatmaps { get; set; }
@ -114,7 +114,7 @@ namespace osu.Game.Screens.Select
private MusicController music { get; set; } private MusicController music { get; set; }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(AudioManager audio, DialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) private void load(AudioManager audio, IDialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender)
{ {
// initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter). // initial value transfer is required for FilterControl (it uses our re-cached bindables in its async load for the initial filter).
transferRulesetValue(); transferRulesetValue();

View File

@ -98,7 +98,7 @@ namespace osu.Game.Tests.Visual
{ {
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
[CanBeNull] [CanBeNull]
private DialogOverlay dialogOverlay { get; set; } private IDialogOverlay dialogOverlay { get; set; }
public new void Undo() => base.Undo(); public new void Undo() => base.Undo();

View File

@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
[Cached] [Cached(typeof(IDialogOverlay))]
protected DialogOverlay DialogOverlay { get; private set; } protected DialogOverlay DialogOverlay { get; private set; }
protected ScreenTestScene() protected ScreenTestScene()