From 2202863e1a1caf323aa341dec967fdaaefd4a237 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 17:49:28 +0900 Subject: [PATCH 01/18] Split out `IPerformFromScreenRunner` to allow for easier testing --- osu.Game/OsuGame.cs | 8 +----- .../Dashboard/CurrentlyPlayingDisplay.cs | 5 ++-- .../Sections/DebugSettings/GeneralSettings.cs | 5 ++-- .../StableDirectoryLocationDialog.cs | 7 ++--- osu.Game/Screens/IPerformFromScreenRunner.cs | 26 +++++++++++++++++++ osu.Game/Screens/Menu/MainMenu.cs | 4 +-- .../Skinning/Editor/SkinEditorSceneLibrary.cs | 7 ++--- 7 files changed, 43 insertions(+), 19 deletions(-) create mode 100644 osu.Game/Screens/IPerformFromScreenRunner.cs diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 73121f6e7d..76ccbe0a08 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -63,7 +63,7 @@ namespace osu.Game /// The full osu! experience. Builds on top of to add menus and binding logic /// for initial components that are generally retrieved via DI. /// - public class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo + public class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo, IPerformFromScreenRunner { /// /// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications). @@ -586,12 +586,6 @@ namespace osu.Game private PerformFromMenuRunner performFromMainMenuTask; - /// - /// Perform an action only after returning to a specific screen as indicated by . - /// Eagerly tries to exit the current screen until it succeeds. - /// - /// The action to perform once we are in the correct state. - /// An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. is used if not specified. public void PerformFromScreen(Action action, IEnumerable validScreens = null) { performFromMainMenuTask?.Cancel(); diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 117de88166..786401b7a8 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -14,6 +14,7 @@ using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Spectator; +using osu.Game.Screens; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; using osu.Game.Users; @@ -106,7 +107,7 @@ namespace osu.Game.Overlays.Dashboard public readonly APIUser User; [Resolved(canBeNull: true)] - private OsuGame game { get; set; } + private IPerformFromScreenRunner performer { get; set; } public PlayingUserPanel(APIUser user) { @@ -140,7 +141,7 @@ namespace osu.Game.Overlays.Dashboard Text = "Watch", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Action = () => game?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), + Action = () => performer?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), Enabled = { Value = User.Id != api.LocalUser.Value.Id } } } diff --git a/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs index 60540a089e..8833420523 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSettings/GeneralSettings.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Game.Localisation; +using osu.Game.Screens; using osu.Game.Screens.Import; namespace osu.Game.Overlays.Settings.Sections.DebugSettings @@ -16,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings protected override LocalisableString Header => DebugSettingsStrings.GeneralHeader; [BackgroundDependencyLoader(true)] - private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, OsuGame game) + private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig, IPerformFromScreenRunner performer) { Children = new Drawable[] { @@ -34,7 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings Add(new SettingsButton { Text = DebugSettingsStrings.ImportFiles, - Action = () => game?.PerformFromScreen(menu => menu.Push(new FileImportScreen())) + Action = () => performer?.PerformFromScreen(menu => menu.Push(new FileImportScreen())) }); } } diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 904c9deaae..4e81cd63b8 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -6,13 +6,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; using osu.Game.Overlays.Dialog; +using osu.Game.Screens; namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class StableDirectoryLocationDialog : PopupDialog { - [Resolved] - private OsuGame game { get; set; } + [Resolved(canBeNull: true)] + private IPerformFromScreenRunner performer { get; set; } public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) { @@ -25,7 +26,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance new PopupDialogOkButton { Text = "Sure! I know where it is located!", - Action = () => Schedule(() => game.PerformFromScreen(screen => screen.Push(new StableDirectorySelectScreen(taskCompletionSource)))) + Action = () => Schedule(() => performer.PerformFromScreen(screen => screen.Push(new StableDirectorySelectScreen(taskCompletionSource)))) }, new PopupDialogCancelButton { diff --git a/osu.Game/Screens/IPerformFromScreenRunner.cs b/osu.Game/Screens/IPerformFromScreenRunner.cs new file mode 100644 index 0000000000..655bebdeb0 --- /dev/null +++ b/osu.Game/Screens/IPerformFromScreenRunner.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Screens; +using osu.Game.Screens.Menu; + +namespace osu.Game.Screens +{ + /// + /// Manages a global screen stack to allow nested components a guarantee of where work is executed. + /// + [Cached] + public interface IPerformFromScreenRunner + { + /// + /// Perform an action only after returning to a specific screen as indicated by . + /// Eagerly tries to exit the current screen until it succeeds. + /// + /// The action to perform once we are in the correct state. + /// An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. is used if not specified. + void PerformFromScreen(Action action, IEnumerable validScreens = null); + } +} diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index e2d79b4015..442a536cf9 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -148,14 +148,14 @@ namespace osu.Game.Screens.Menu } [Resolved(canBeNull: true)] - private OsuGame game { get; set; } + private IPerformFromScreenRunner performer { get; set; } private void confirmAndExit() { if (exitConfirmed) return; exitConfirmed = true; - game?.PerformFromScreen(menu => menu.Exit()); + performer?.PerformFromScreen(menu => menu.Exit()); } private void preloadSongSelect() diff --git a/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs index 0808cd157f..4507526806 100644 --- a/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs +++ b/osu.Game/Skinning/Editor/SkinEditorSceneLibrary.cs @@ -15,6 +15,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Screens; using osu.Game.Screens.Play; using osu.Game.Screens.Select; using osuTK; @@ -28,7 +29,7 @@ namespace osu.Game.Skinning.Editor private const float padding = 10; [Resolved(canBeNull: true)] - private OsuGame game { get; set; } + private IPerformFromScreenRunner performer { get; set; } [Resolved] private IBindable ruleset { get; set; } @@ -75,7 +76,7 @@ namespace osu.Game.Skinning.Editor Text = "Song Select", Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Action = () => game?.PerformFromScreen(screen => + Action = () => performer?.PerformFromScreen(screen => { if (screen is SongSelect) return; @@ -88,7 +89,7 @@ namespace osu.Game.Skinning.Editor Text = "Gameplay", Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Action = () => game?.PerformFromScreen(screen => + Action = () => performer?.PerformFromScreen(screen => { if (screen is Player) return; From e3153132666a911dc2ac47d759d6a7654b128f59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 18:09:14 +0900 Subject: [PATCH 02/18] Split out `IDialogOverlay` to allow for easier testing --- .../TestSceneManageCollectionsDialog.cs | 2 +- .../Visual/Navigation/TestSceneOsuGame.cs | 2 +- .../Navigation/TestScenePerformFromScreen.cs | 11 ++++--- .../Navigation/TestSceneScreenNavigation.cs | 13 ++++---- .../TestSceneDeleteLocalScore.cs | 2 +- .../UserInterface/TestSceneDialogOverlay.cs | 2 +- .../Collections/DrawableCollectionListItem.cs | 2 +- osu.Game/Database/LegacyImportManager.cs | 2 +- osu.Game/Online/Chat/ExternalLinkOpener.cs | 2 +- .../Online/Leaderboards/LeaderboardScore.cs | 2 +- osu.Game/Overlays/Dialog/PopupDialog.cs | 2 +- osu.Game/Overlays/DialogOverlay.cs | 2 +- osu.Game/Overlays/IDialogOverlay.cs | 31 +++++++++++++++++++ .../Sections/Maintenance/GeneralSettings.cs | 2 +- .../Maintenance/MigrationSelectScreen.cs | 2 +- osu.Game/PerformFromMenuRunner.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Menu/MainMenu.cs | 2 +- osu.Game/Screens/Menu/StorageErrorDialog.cs | 2 +- .../Multiplayer/MultiplayerMatchSubScreen.cs | 2 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 4 +-- osu.Game/Tests/Visual/EditorTestScene.cs | 2 +- osu.Game/Tests/Visual/ScreenTestScene.cs | 2 +- 24 files changed, 66 insertions(+), 33 deletions(-) create mode 100644 osu.Game/Overlays/IDialogOverlay.cs diff --git a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs index e40dd58663..51ca55f37f 100644 --- a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Collections }); Dependencies.Cache(manager); - Dependencies.Cache(dialogOverlay); + Dependencies.CacheAs(dialogOverlay); } [SetUp] diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index b8d1636ea0..921d088fc7 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Navigation typeof(LoginOverlay), typeof(MusicController), typeof(AccountCreationOverlay), - typeof(DialogOverlay), + typeof(IDialogOverlay), typeof(ScreenshotManager) }; diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index f045e3a3e8..c4099b55c7 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Framework.Testing; @@ -113,12 +114,12 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("did not perform", () => !actionPerformed); AddAssert("only one exit attempt", () => blocker.ExitAttempts == 1); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); if (confirmed) { AddStep("accept dialog", () => InputManager.Key(Key.Number1)); - AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); + AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); AddUntilStep("did perform", () => actionPerformed); } else @@ -145,7 +146,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == blocker2); AddAssert("did not perform", () => !actionPerformed); @@ -187,7 +188,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack); AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); @@ -221,7 +222,7 @@ namespace osu.Game.Tests.Visual.Navigation public class DialogBlockingScreen : OsuScreen { [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private int dialogDisplayCount; diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 394976eb43..0b7242eee0 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -6,6 +6,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; @@ -200,10 +201,10 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("choose clear all scores", () => InputManager.Key(Key.Number4)); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); - AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); AddStep("confirm deletion", () => InputManager.Key(Key.Number1)); - AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); + AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find(score.ID)?.DeletePending == true)); @@ -246,10 +247,10 @@ namespace osu.Game.Tests.Visual.Navigation InputManager.Click(MouseButton.Left); }); - AddUntilStep("wait for dialog display", () => Game.Dependencies.Get().IsLoaded); - AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); + AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + AddUntilStep("wait for dialog", () => Game.Dependencies.Get().CurrentDialog != null); AddStep("confirm deletion", () => InputManager.Key(Key.Number1)); - AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); + AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get().CurrentDialog == null); AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find(score.ID)?.DeletePending == true)); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 72dfc53c01..1350052ae6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.UserInterface private BeatmapInfo beatmapInfo; - [Cached] + [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay; public TestSceneDeleteLocalScore() diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index 405461eec8..17234f4022 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -11,7 +11,7 @@ using osu.Game.Overlays.Dialog; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneDialogOverlay : OsuTestScene + public class TestSceneIDialogOverlay : OsuTestScene { private DialogOverlay overlay; diff --git a/osu.Game/Collections/DrawableCollectionListItem.cs b/osu.Game/Collections/DrawableCollectionListItem.cs index c4cb040b52..5a20b7e7bd 100644 --- a/osu.Game/Collections/DrawableCollectionListItem.cs +++ b/osu.Game/Collections/DrawableCollectionListItem.cs @@ -158,7 +158,7 @@ namespace osu.Game.Collections public Func IsTextBoxHovered; [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private CollectionManager collectionManager { get; set; } diff --git a/osu.Game/Database/LegacyImportManager.cs b/osu.Game/Database/LegacyImportManager.cs index 4dc26b18bb..59394c2952 100644 --- a/osu.Game/Database/LegacyImportManager.cs +++ b/osu.Game/Database/LegacyImportManager.cs @@ -40,7 +40,7 @@ namespace osu.Game.Database private OsuGame game { get; set; } [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private DesktopGameHost desktopGameHost { get; set; } diff --git a/osu.Game/Online/Chat/ExternalLinkOpener.cs b/osu.Game/Online/Chat/ExternalLinkOpener.cs index 328b43c4e8..20d8459132 100644 --- a/osu.Game/Online/Chat/ExternalLinkOpener.cs +++ b/osu.Game/Online/Chat/ExternalLinkOpener.cs @@ -17,7 +17,7 @@ namespace osu.Game.Online.Chat private GameHost host { get; set; } [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private Bindable externalLinkWarning; diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index ddd9d9a2b2..eecb7ff6b3 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -64,7 +64,7 @@ namespace osu.Game.Online.Leaderboards private List statisticsLabels; [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private SongSelect songSelect { get; set; } diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 6159ab59f0..9fd1a32e84 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -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. - // This is used by the DialogOverlay to know when the dialog was dismissed. + // This is used by the IDialogOverlay to know when the dialog was dismissed. Show(); } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 9dea1ca00a..15d89a561a 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -14,7 +14,7 @@ using osu.Game.Audio.Effects; namespace osu.Game.Overlays { - public class DialogOverlay : OsuFocusedOverlayContainer + public class DialogOverlay : OsuFocusedOverlayContainer, IDialogOverlay { private readonly Container dialogContainer; diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs new file mode 100644 index 0000000000..07f28a7204 --- /dev/null +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -0,0 +1,31 @@ +#nullable enable +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Overlays.Dialog; + +namespace osu.Game.Overlays +{ + /// + /// A global overlay that can show popup dialogs. + /// + [Cached] + public interface IDialogOverlay + { + /// + /// Push a new dialog for display. + /// + /// + /// This will immediate dismiss any already displayed dialog (cancelling the action). + /// If the dialog instance provided is already displayed, it will be a noop. + /// + /// The dialog to be presented. + void Push(PopupDialog dialog); + + /// + /// The currently displayed dialog, if any. + /// + PopupDialog? CurrentDialog { get; } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index aa02d086f4..be4b0decd9 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private SettingsButton undeleteButton; [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) { diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs index 0304a4291a..3cb5521e51 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private OsuGameBase game { get; set; } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } protected override DirectoryInfo InitialPath => new DirectoryInfo(storage.GetFullPath(string.Empty)).Parent; diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index 528470768c..7039d5379c 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -26,7 +26,7 @@ namespace osu.Game private NotificationOverlay notifications { get; set; } [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(canBeNull: true)] private OsuGame game { get; set; } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 57f7429e06..255571dfdb 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -84,7 +84,7 @@ namespace osu.Game.Screens.Edit private Storage storage { get; set; } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(canBeNull: true)] private NotificationOverlay notifications { get; set; } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index e2d79b4015..9e6af860fc 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Menu private IAPIProvider api { get; set; } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private BackgroundScreenDefault background; diff --git a/osu.Game/Screens/Menu/StorageErrorDialog.cs b/osu.Game/Screens/Menu/StorageErrorDialog.cs index 250623ec68..f4c77d5d8f 100644 --- a/osu.Game/Screens/Menu/StorageErrorDialog.cs +++ b/osu.Game/Screens/Menu/StorageErrorDialog.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.Menu public class StorageErrorDialog : PopupDialog { [Resolved] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } public StorageErrorDialog(OsuStorage storage, OsuStorageError error) { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 7ef0bb5923..0dbf73fc5b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -238,7 +238,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer } [Resolved(canBeNull: true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } private bool exitConfirmed; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 618c5cf5ec..2d70b1aecb 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select.Carousel private Action viewDetails; [Resolved(CanBeNull = true)] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } [Resolved(CanBeNull = true)] private CollectionManager collectionManager { get; set; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 2d1a2bce4e..dc505063d1 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Select protected Container LeftArea { get; private set; } private BeatmapInfoWedge beatmapInfoWedge; - private DialogOverlay dialogOverlay; + private IDialogOverlay dialogOverlay; [Resolved] private BeatmapManager beatmaps { get; set; } @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Select private MusicController music { get; set; } [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). transferRulesetValue(); diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 51221cb8fe..ca12bc73fe 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Visual { [Resolved(canBeNull: true)] [CanBeNull] - private DialogOverlay dialogOverlay { get; set; } + private IDialogOverlay dialogOverlay { get; set; } public new void Undo() => base.Undo(); diff --git a/osu.Game/Tests/Visual/ScreenTestScene.cs b/osu.Game/Tests/Visual/ScreenTestScene.cs index b6f6ca6daa..e9069d8c9c 100644 --- a/osu.Game/Tests/Visual/ScreenTestScene.cs +++ b/osu.Game/Tests/Visual/ScreenTestScene.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual protected override Container Content => content; - [Cached] + [Cached(typeof(IDialogOverlay))] protected DialogOverlay DialogOverlay { get; private set; } protected ScreenTestScene() From 25f1f772f3d6cad2a0bb9728057e106588555bd0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 18:36:22 +0900 Subject: [PATCH 03/18] Allow caching via `loadComponentSingleFile` with interface specification --- osu.Game/OsuGame.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 73121f6e7d..f8bc32431c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -825,7 +825,7 @@ namespace osu.Game }, rightFloatingOverlayContent.Add, true); loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); - loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); + loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(CreateHighPerformanceSession(), Add); @@ -982,12 +982,15 @@ namespace osu.Game /// The component to load. /// An action to invoke on load completion (generally to add the component to the hierarchy). /// Whether to cache the component as type into the game dependencies before any scheduling. - private T loadComponentSingleFile(T component, Action loadCompleteAction, bool cache = false) - where T : Drawable + private T loadComponentSingleFile(T component, Action loadCompleteAction, bool cache = false) + where T : class { if (cache) dependencies.CacheAs(component); + var drawableComponent = component as Drawable; + Debug.Assert(drawableComponent != null); + if (component is OsuFocusedOverlayContainer overlay) focusedOverlays.Add(overlay); @@ -1011,7 +1014,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 // throwing an exception. To avoid this, the call is scheduled on the update thread, which does not run if IsDisposed = true Task task = null; - var del = new ScheduledDelegate(() => task = LoadComponentAsync(component, loadCompleteAction)); + var del = new ScheduledDelegate(() => task = LoadComponentAsync(drawableComponent, loadCompleteAction)); Scheduler.Add(del); // The delegate won't complete if OsuGame has been disposed in the meantime From fea4632e839207ae1b183878aff42a437e271fa6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 20:04:19 +0900 Subject: [PATCH 04/18] Convert assert to hard throw --- osu.Game/OsuGame.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f8bc32431c..e10da39055 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -988,8 +988,7 @@ namespace osu.Game if (cache) dependencies.CacheAs(component); - var drawableComponent = component as Drawable; - Debug.Assert(drawableComponent != null); + var drawableComponent = component as Drawable ?? throw new ArgumentException($"Component must be a {nameof(Drawable)}", nameof(component)); if (component is OsuFocusedOverlayContainer overlay) focusedOverlays.Add(overlay); From d86e3abf2c5d4857062cf23d680fa40f94006a0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 20:04:38 +0900 Subject: [PATCH 05/18] Fix invalid rename --- osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index 17234f4022..405461eec8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -11,7 +11,7 @@ using osu.Game.Overlays.Dialog; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public class TestSceneIDialogOverlay : OsuTestScene + public class TestSceneDialogOverlay : OsuTestScene { private DialogOverlay overlay; From 72a33c0926dde589b5dc1c2ffabdcf5184bdb474 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 20:06:08 +0900 Subject: [PATCH 06/18] Split out step for dialog overlay load wait --- .../Visual/Navigation/TestScenePerformFromScreen.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs index c4099b55c7..559e6ddd89 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePerformFromScreen.cs @@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("did not perform", () => !actionPerformed); AddAssert("only one exit attempt", () => blocker.ExitAttempts == 1); - AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + waitForDialogOverlayLoad(); if (confirmed) { @@ -146,7 +146,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + waitForDialogOverlayLoad(); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == blocker2); AddAssert("did not perform", () => !actionPerformed); @@ -188,7 +188,7 @@ namespace osu.Game.Tests.Visual.Navigation AddWaitStep("wait a bit", 10); - AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + waitForDialogOverlayLoad(); AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack); AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker); @@ -212,6 +212,8 @@ namespace osu.Game.Tests.Visual.Navigation } } + private void waitForDialogOverlayLoad() => AddUntilStep("wait for dialog overlay loaded", () => ((Drawable)Game.Dependencies.Get()).IsLoaded); + private void importAndWaitForSongSelect() { AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); From 043599081bc29f81fa6888d48d214d1cc505edb3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 19:59:57 +0900 Subject: [PATCH 07/18] Split out `INotificationOverlay` to allow for easier testing --- .../Security/ElevatedPrivilegesChecker.cs | 2 +- osu.Desktop/Updater/SquirrelUpdateManager.cs | 2 +- osu.Game.Tests/Visual/Menus/IntroTestScene.cs | 2 +- .../Visual/Navigation/TestSceneOsuGame.cs | 2 +- .../Visual/Online/TestSceneMessageNotifier.cs | 2 +- osu.Game/Database/EFToRealmMigrator.cs | 2 +- osu.Game/Graphics/ScreenshotManager.cs | 2 +- osu.Game/Online/Chat/MessageNotifier.cs | 4 +-- osu.Game/OsuGame.cs | 2 +- .../BeatmapSet/Buttons/FavouriteButton.cs | 2 +- osu.Game/Overlays/INotificationOverlay.cs | 26 +++++++++++++++++++ osu.Game/Overlays/NotificationOverlay.cs | 6 +---- .../Sections/General/UpdateSettings.cs | 2 +- .../Maintenance/MigrationRunScreen.cs | 2 +- osu.Game/PerformFromMenuRunner.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- osu.Game/Screens/Play/HUDOverlay.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 4 +-- osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- osu.Game/Updater/UpdateManager.cs | 4 +-- 22 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 osu.Game/Overlays/INotificationOverlay.cs diff --git a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs index 8f3ad853dc..ba37a14442 100644 --- a/osu.Desktop/Security/ElevatedPrivilegesChecker.cs +++ b/osu.Desktop/Security/ElevatedPrivilegesChecker.cs @@ -19,7 +19,7 @@ namespace osu.Desktop.Security public class ElevatedPrivilegesChecker : Component { [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } private bool elevated; diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index b307146b10..bae850fce1 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -25,7 +25,7 @@ namespace osu.Desktop.Updater public class SquirrelUpdateManager : osu.Game.Updater.UpdateManager { private UpdateManager updateManager; - private NotificationOverlay notificationOverlay; + private INotificationOverlay notificationOverlay; public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited(); diff --git a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs index 82accceb23..c68cd39c65 100644 --- a/osu.Game.Tests/Visual/Menus/IntroTestScene.cs +++ b/osu.Game.Tests/Visual/Menus/IntroTestScene.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual.Menus private IntroScreen intro; - [Cached] + [Cached(typeof(INotificationOverlay))] private NotificationOverlay notifications; private ScheduledDelegate trackResetDelegate; diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs index 921d088fc7..0f8337deb6 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneOsuGame.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Navigation typeof(OsuLogo), typeof(IdleTracker), typeof(OnScreenDisplay), - typeof(NotificationOverlay), + typeof(INotificationOverlay), typeof(BeatmapListingOverlay), typeof(DashboardOverlay), typeof(NewsOverlay), diff --git a/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs b/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs index 2c253650d5..79f62a16e3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneMessageNotifier.cs @@ -200,7 +200,7 @@ namespace osu.Game.Tests.Visual.Online [Cached] public ChannelManager ChannelManager { get; } = new ChannelManager(); - [Cached] + [Cached(typeof(INotificationOverlay))] public NotificationOverlay NotificationOverlay { get; } = new NotificationOverlay { Anchor = Anchor.TopRight, diff --git a/osu.Game/Database/EFToRealmMigrator.cs b/osu.Game/Database/EFToRealmMigrator.cs index ae73e13b77..4e98b7d3d2 100644 --- a/osu.Game/Database/EFToRealmMigrator.cs +++ b/osu.Game/Database/EFToRealmMigrator.cs @@ -52,7 +52,7 @@ namespace osu.Game.Database private OsuConfigManager config { get; set; } = null!; [Resolved] - private NotificationOverlay notificationOverlay { get; set; } = null!; + private INotificationOverlay notificationOverlay { get; set; } = null!; [Resolved] private OsuGame game { get; set; } = null!; diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index b0f20de685..a2f1a3d7b9 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -43,7 +43,7 @@ namespace osu.Game.Graphics private Storage storage; [Resolved] - private NotificationOverlay notificationOverlay { get; set; } + private INotificationOverlay notificationOverlay { get; set; } private Sample shutter; diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs index 20c33db61f..ca6082e19b 100644 --- a/osu.Game/Online/Chat/MessageNotifier.cs +++ b/osu.Game/Online/Chat/MessageNotifier.cs @@ -24,7 +24,7 @@ namespace osu.Game.Online.Chat public class MessageNotifier : Component { [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved] private ChatOverlay chatOverlay { get; set; } @@ -170,7 +170,7 @@ namespace osu.Game.Online.Chat public override bool IsImportant => false; [BackgroundDependencyLoader] - private void load(OsuColour colours, ChatOverlay chatOverlay, NotificationOverlay notificationOverlay) + private void load(OsuColour colours, ChatOverlay chatOverlay, INotificationOverlay notificationOverlay) { IconBackground.Colour = colours.PurpleDark; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e10da39055..45d76cc15b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -778,7 +778,7 @@ namespace osu.Game loadComponentSingleFile(onScreenDisplay, Add, true); - loadComponentSingleFile(Notifications.With(d => + loadComponentSingleFile(Notifications.With(d => { d.Anchor = Anchor.TopRight; d.Origin = Anchor.TopRight; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index 8fe7450873..28100e5fff 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons } [BackgroundDependencyLoader(true)] - private void load(IAPIProvider api, NotificationOverlay notifications) + private void load(IAPIProvider api, INotificationOverlay notifications) { SpriteIcon icon; diff --git a/osu.Game/Overlays/INotificationOverlay.cs b/osu.Game/Overlays/INotificationOverlay.cs new file mode 100644 index 0000000000..432dd2be54 --- /dev/null +++ b/osu.Game/Overlays/INotificationOverlay.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Overlays.Notifications; + +namespace osu.Game.Overlays +{ + /// + /// An overlay which is capable of showing notifications to the user. + /// + [Cached] + public interface INotificationOverlay + { + /// + /// Post a new notification for display. + /// + /// The notification to display. + void Post(Notification notification); + + /// + /// Hide the overlay, if it is currently visible. + /// + void Hide(); + } +} diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index e4e3931048..0e8fbc6680 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -19,7 +19,7 @@ using osu.Game.Localisation; namespace osu.Game.Overlays { - public class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent + public class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent, INotificationOverlay { public string IconTexture => "Icons/Hexacons/notification"; public LocalisableString Title => NotificationsStrings.HeaderTitle; @@ -111,10 +111,6 @@ namespace osu.Game.Overlays private double? lastSamplePlayback; - /// - /// Post a new notification for display. - /// - /// The notification to display. public void Post(Notification notification) => postScheduler.Add(() => { ++runningDepth; diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 0b4eca6379..5bc88b8692 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.General private SettingsButton checkForUpdatesButton; [Resolved(CanBeNull = true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [BackgroundDependencyLoader(true)] private void load(Storage storage, OsuConfigManager config, OsuGame game) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs index fb7ff0dbd1..10d6f0aef5 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationRunScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance private OsuGame game { get; set; } [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved] private Storage storage { get; set; } diff --git a/osu.Game/PerformFromMenuRunner.cs b/osu.Game/PerformFromMenuRunner.cs index 7039d5379c..ae9879fb5a 100644 --- a/osu.Game/PerformFromMenuRunner.cs +++ b/osu.Game/PerformFromMenuRunner.cs @@ -23,7 +23,7 @@ namespace osu.Game private readonly Func getCurrentScreen; [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved] private IDialogOverlay dialogOverlay { get; set; } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 255571dfdb..602b7563ac 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Edit private IDialogOverlay dialogOverlay { get; set; } [Resolved(canBeNull: true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } public readonly Bindable Mode = new Bindable(); diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 8eeb90a3fd..598c441042 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -118,7 +118,7 @@ namespace osu.Game.Screens.Menu private IAPIProvider api { get; set; } [Resolved(CanBeNull = true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } [Resolved(CanBeNull = true)] private LoginOverlay loginOverlay { get; set; } diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index e569b0e517..5575d3681f 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -171,7 +171,7 @@ namespace osu.Game.Screens.Menu } [Resolved] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } private void ensureEventuallyArrivingAtMenu() { diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 628452fbc8..676a08b434 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader(true)] - private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) + private void load(OsuConfigManager config, INotificationOverlay notificationOverlay) { if (drawableRuleset != null) { diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index ba720af2a1..b72ec373d7 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -515,7 +515,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audioManager, NotificationOverlay notificationOverlay, VolumeOverlay volumeOverlay) + private void load(OsuColour colours, AudioManager audioManager, INotificationOverlay notificationOverlay, VolumeOverlay volumeOverlay) { Icon = FontAwesome.Solid.VolumeMute; IconBackground.Colour = colours.RedDark; @@ -567,7 +567,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(OsuColour colours, NotificationOverlay notificationOverlay) + private void load(OsuColour colours, INotificationOverlay notificationOverlay) { Icon = FontAwesome.Solid.BatteryQuarter; IconBackground.Colour = colours.RedDark; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 593436bbb7..4a991ffc66 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Select private OsuScreen playerLoader; [Resolved(CanBeNull = true)] - private NotificationOverlay notifications { get; set; } + private INotificationOverlay notifications { get; set; } public override bool AllowExternalScreenChange => true; diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 9b9f354d23..c17d8304b9 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -31,7 +31,7 @@ namespace osu.Game.Updater private OsuGameBase game { get; set; } [Resolved] - protected NotificationOverlay Notifications { get; private set; } + protected INotificationOverlay Notifications { get; private set; } protected override void LoadComplete() { @@ -94,7 +94,7 @@ namespace osu.Game.Updater } [BackgroundDependencyLoader] - private void load(OsuColour colours, ChangelogOverlay changelog, NotificationOverlay notificationOverlay) + private void load(OsuColour colours, ChangelogOverlay changelog, INotificationOverlay notificationOverlay) { Icon = FontAwesome.Solid.CheckSquare; IconBackground.Colour = colours.BlueDark; From d29312e0709eb11e63effe51fb6c806966e3689d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 21:47:47 +0900 Subject: [PATCH 08/18] Fix rider incompetency --- osu.Game/Overlays/IDialogOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs index 07f28a7204..dedc164510 100644 --- a/osu.Game/Overlays/IDialogOverlay.cs +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -1,7 +1,8 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . 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; From aaf5577e6a035606f19e3fd249b7a4f464f35a13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 21:48:46 +0900 Subject: [PATCH 09/18] Remove unnecessary `canBeNull` specification --- .../Sections/Maintenance/StableDirectoryLocationDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs index 4e81cd63b8..b16fd9a5a1 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectoryLocationDialog.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class StableDirectoryLocationDialog : PopupDialog { - [Resolved(canBeNull: true)] + [Resolved] private IPerformFromScreenRunner performer { get; set; } public StableDirectoryLocationDialog(TaskCompletionSource taskCompletionSource) From 5d5e46ede71bd05ac9846da73eb03119f96cad7b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Apr 2022 21:47:47 +0900 Subject: [PATCH 10/18] Fix rider incompetency --- osu.Game/Overlays/IDialogOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs index 07f28a7204..dedc164510 100644 --- a/osu.Game/Overlays/IDialogOverlay.cs +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -1,7 +1,8 @@ -#nullable enable // Copyright (c) ppy Pty Ltd . 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; From 3cbc6cd29767a7d47ba624dc1680c22f4a02cedd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 21:04:22 +0300 Subject: [PATCH 11/18] Update further tests to cache using `IDialogOverlay` --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 2 +- osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs | 2 +- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs | 2 +- .../Visual/SongSelect/TestSceneUserTopScoreContainer.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index d077868175..6818147da4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online Dependencies.Cache(chatManager); Dependencies.Cache(new ChatOverlay()); - Dependencies.Cache(dialogOverlay); + Dependencies.CacheAs(dialogOverlay); } [SetUp] diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index f9c9b2a68b..2106043544 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Settings Depth = -1 }); - Dependencies.Cache(dialogOverlay); + Dependencies.CacheAs(dialogOverlay); } } } diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs index 1ed6648131..3b15ee9c45 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.SongSelect { private readonly FailableLeaderboard leaderboard; - [Cached] + [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay; private ScoreManager scoreManager; diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs index dd7f9951bf..c71e54e9a8 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.SongSelect { public class TestSceneUserTopScoreContainer : OsuTestScene { - [Cached] + [Cached(typeof(IDialogOverlay))] private readonly DialogOverlay dialogOverlay; public TestSceneUserTopScoreContainer() From 16a0efc5621ba8a751f874bb8fef91218766efa2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 21:06:27 +0300 Subject: [PATCH 12/18] Cache `IDialogOverlay` as its type in interface --- osu.Game/Overlays/IDialogOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/IDialogOverlay.cs b/osu.Game/Overlays/IDialogOverlay.cs index dedc164510..1c6a84cd64 100644 --- a/osu.Game/Overlays/IDialogOverlay.cs +++ b/osu.Game/Overlays/IDialogOverlay.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays /// /// A global overlay that can show popup dialogs. /// - [Cached] + [Cached(typeof(IDialogOverlay))] public interface IDialogOverlay { /// From 1fc28552b54400ca6e49f40ad5e40f4c88d3bd6e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 18 Apr 2022 21:07:20 +0300 Subject: [PATCH 13/18] Minor reword inline comment --- osu.Game/Overlays/Dialog/PopupDialog.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 9fd1a32e84..d08b6b7beb 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -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. - // This is used by the IDialogOverlay to know when the dialog was dismissed. + // This is used by the dialog overlay to know when the dialog was dismissed. Show(); } From c4d89b8c3c446d6440b023c2fb5508da4c99add6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 05:57:03 +0900 Subject: [PATCH 14/18] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index a8952df329..8b22df418f 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3246255815..0a102510e7 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,7 +35,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 668d23f82e..2f0facea18 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -61,7 +61,7 @@ - + @@ -84,7 +84,7 @@ - + From 94335c293848a5475aa0df3f8c71f6726a544ec8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 19 Apr 2022 00:18:10 +0300 Subject: [PATCH 15/18] Update further usages to cache/resolve via interface --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 4 ++-- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +- osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index bae850fce1..c09cce1235 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -39,9 +39,9 @@ namespace osu.Desktop.Updater private readonly SquirrelLogger squirrelLogger = new SquirrelLogger(); [BackgroundDependencyLoader] - private void load(NotificationOverlay notification) + private void load(INotificationOverlay notifications) { - notificationOverlay = notification; + notificationOverlay = notifications; SquirrelLocator.CurrentMutable.Register(() => squirrelLogger, typeof(ILogger)); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 958d617d63..950c755cc1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private SessionStatics sessionStatics { get; set; } - [Cached] + [Cached(typeof(INotificationOverlay))] private readonly NotificationOverlay notificationOverlay; [Cached] diff --git a/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs b/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs index a68090504d..ac0956502e 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneMigrationScreens.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Settings { public class TestSceneMigrationScreens : ScreenTestScene { - [Cached] + [Cached(typeof(INotificationOverlay))] private readonly NotificationOverlay notifications; public TestSceneMigrationScreens() diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index b72ec373d7..aea39bc8ff 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Play private EpilepsyWarning? epilepsyWarning; [Resolved(CanBeNull = true)] - private NotificationOverlay? notificationOverlay { get; set; } + private INotificationOverlay? notificationOverlay { get; set; } [Resolved(CanBeNull = true)] private VolumeOverlay? volumeOverlay { get; set; } From 83e89b3e80c737b626fe1962d2652a64a381058e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 11:10:11 +0900 Subject: [PATCH 16/18] Add unread count to `INotificationOverlay` and fix toolbar button DI logic --- .../Visual/Menus/TestSceneToolbar.cs | 18 +++++++++++++----- osu.Game/Overlays/INotificationOverlay.cs | 6 ++++++ osu.Game/Overlays/NotificationOverlay.cs | 6 ++++-- .../Toolbar/ToolbarNotificationButton.cs | 8 ++++---- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs index c65595d82e..dbc7e54b5e 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneToolbar.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using Moq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -22,6 +23,17 @@ namespace osu.Game.Tests.Visual.Menus [Resolved] private IRulesetStore rulesets { get; set; } + private readonly Mock notifications = new Mock(); + + private readonly BindableInt unreadNotificationCount = new BindableInt(); + + [BackgroundDependencyLoader] + private void load() + { + Dependencies.CacheAs(notifications.Object); + notifications.SetupGet(n => n.UnreadCount).Returns(unreadNotificationCount); + } + [SetUp] public void SetUp() => Schedule(() => { @@ -31,10 +43,6 @@ namespace osu.Game.Tests.Visual.Menus [Test] public void TestNotificationCounter() { - ToolbarNotificationButton notificationButton = null; - - AddStep("retrieve notification button", () => notificationButton = toolbar.ChildrenOfType().Single()); - setNotifications(1); setNotifications(2); setNotifications(3); @@ -43,7 +51,7 @@ namespace osu.Game.Tests.Visual.Menus void setNotifications(int count) => AddStep($"set notification count to {count}", - () => notificationButton.NotificationCount.Value = count); + () => unreadNotificationCount.Value = count); } [TestCase(false)] diff --git a/osu.Game/Overlays/INotificationOverlay.cs b/osu.Game/Overlays/INotificationOverlay.cs index 432dd2be54..1d8e33ea3a 100644 --- a/osu.Game/Overlays/INotificationOverlay.cs +++ b/osu.Game/Overlays/INotificationOverlay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Game.Overlays.Notifications; namespace osu.Game.Overlays @@ -22,5 +23,10 @@ namespace osu.Game.Overlays /// Hide the overlay, if it is currently visible. /// void Hide(); + + /// + /// Current number of unread notifications. + /// + IBindable UnreadCount { get; } } } diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 0e8fbc6680..2b5909f45a 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -99,7 +99,9 @@ namespace osu.Game.Overlays OverlayActivationMode.BindValueChanged(_ => updateProcessingMode(), true); } - public readonly BindableInt UnreadCount = new BindableInt(); + public IBindable UnreadCount => unreadCount; + + private readonly BindableInt unreadCount = new BindableInt(); private int runningDepth; @@ -180,7 +182,7 @@ namespace osu.Game.Overlays private void updateCounts() { - UnreadCount.Value = sections.Select(c => c.UnreadCount).Sum(); + unreadCount.Value = sections.Select(c => c.UnreadCount).Sum(); } private void markAllRead() diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs index 79d0fc74c1..313a2bc3f4 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Toolbar { protected override Anchor TooltipAnchor => Anchor.TopRight; - public BindableInt NotificationCount = new BindableInt(); + public IBindable NotificationCount = new BindableInt(); private readonly CountCircle countDisplay; @@ -36,10 +36,10 @@ namespace osu.Game.Overlays.Toolbar }); } - [BackgroundDependencyLoader(true)] - private void load(NotificationOverlay notificationOverlay) + [BackgroundDependencyLoader] + private void load(INotificationOverlay notificationOverlay) { - StateContainer = notificationOverlay; + StateContainer = notificationOverlay as NotificationOverlay; if (notificationOverlay != null) NotificationCount.BindTo(notificationOverlay.UnreadCount); From feeff1647697ef1c45156091cce88da20464caee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 14:30:45 +0900 Subject: [PATCH 17/18] Add debug language to help with localisation efforts The idea is to allow a developer to immediately see which text on a component or screen has already got localisation support. It can be a bit of a challenge to see this when creating a new component that doesn't yet have any translations populated. Curious to hear thoughts on this. I could see it working very well as a visual tests checkbox (implemented at o!f side), potentially in addition to having this at the game level, or replacing this PR. --- .../Localisation/DebugLocalisationStore.cs | 30 +++++++++++++++++++ osu.Game/Localisation/Language.cs | 7 ++++- osu.Game/OsuGame.cs | 8 +++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Localisation/DebugLocalisationStore.cs diff --git a/osu.Game/Localisation/DebugLocalisationStore.cs b/osu.Game/Localisation/DebugLocalisationStore.cs new file mode 100644 index 0000000000..99dca6265e --- /dev/null +++ b/osu.Game/Localisation/DebugLocalisationStore.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public class DebugLocalisationStore : ILocalisationStore + { + public string Get(string lookup) => $@"[[{lookup.Substring(lookup.LastIndexOf('.') + 1)}]]"; + + public Task GetAsync(string lookup, CancellationToken cancellationToken = default) => Task.FromResult(Get(lookup)); + + public Stream GetStream(string name) => throw new NotImplementedException(); + + public IEnumerable GetAvailableResources() => throw new NotImplementedException(); + + public CultureInfo EffectiveCulture { get; } = new CultureInfo(@"debug"); + + public void Dispose() + { + } + } +} diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index ab96d01c82..c98586457e 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -110,6 +110,11 @@ namespace osu.Game.Localisation // zh_hk, [Description(@"繁體中文(台灣)")] - zh_hant + zh_hant, + +#if DEBUG + [Description(@"Debug (show raw keys)")] + DebugLocalisation +#endif } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index eee86de7ff..b591b09584 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -628,6 +628,14 @@ namespace osu.Game foreach (var language in Enum.GetValues(typeof(Language)).OfType()) { +#if DEBUG + if (language == Language.DebugLocalisation) + { + Localisation.AddLanguage(Language.DebugLocalisation.ToString(), new DebugLocalisationStore()); + continue; + } +#endif + string cultureCode = language.ToCultureCode(); try From a195d4f5aa5a132bf6860e23ab04f327cb84b8a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Apr 2022 16:49:41 +0900 Subject: [PATCH 18/18] Use a culture name that doesn't cause everything to fall over --- osu.Game/Localisation/DebugLocalisationStore.cs | 2 +- osu.Game/Localisation/Language.cs | 2 +- osu.Game/OsuGame.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/DebugLocalisationStore.cs b/osu.Game/Localisation/DebugLocalisationStore.cs index 99dca6265e..2b114b1bd8 100644 --- a/osu.Game/Localisation/DebugLocalisationStore.cs +++ b/osu.Game/Localisation/DebugLocalisationStore.cs @@ -21,7 +21,7 @@ namespace osu.Game.Localisation public IEnumerable GetAvailableResources() => throw new NotImplementedException(); - public CultureInfo EffectiveCulture { get; } = new CultureInfo(@"debug"); + public CultureInfo EffectiveCulture { get; } = CultureInfo.CurrentCulture; public void Dispose() { diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs index c98586457e..c13a1a10cb 100644 --- a/osu.Game/Localisation/Language.cs +++ b/osu.Game/Localisation/Language.cs @@ -114,7 +114,7 @@ namespace osu.Game.Localisation #if DEBUG [Description(@"Debug (show raw keys)")] - DebugLocalisation + debug #endif } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b591b09584..ea21954b9c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -629,9 +629,9 @@ namespace osu.Game foreach (var language in Enum.GetValues(typeof(Language)).OfType()) { #if DEBUG - if (language == Language.DebugLocalisation) + if (language == Language.debug) { - Localisation.AddLanguage(Language.DebugLocalisation.ToString(), new DebugLocalisationStore()); + Localisation.AddLanguage(Language.debug.ToString(), new DebugLocalisationStore()); continue; } #endif