diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index 643f4131dc..2eb6d3f80e 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -358,7 +358,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.Equals(target));
// this is an important check, to make sure updateComponentFromBeatmap() was actually run
- AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo == target);
+ AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.Equals(target));
}
[Test]
@@ -390,7 +390,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("has correct ruleset", () => Ruleset.Value.ID == 0);
// this is an important check, to make sure updateComponentFromBeatmap() was actually run
- AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo == target);
+ AddUntilStep("selection shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap.BeatmapInfo.Equals(target));
}
[Test]
@@ -781,7 +781,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
- AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo == groupIcon.Items.First().Beatmap);
+ AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo.Equals(groupIcon.Items.First().Beatmap));
}
[Test]
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs
index cc4a57fb83..f5cba2c900 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs
@@ -2,7 +2,9 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
+using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
+using osu.Framework.Testing;
using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
@@ -11,13 +13,20 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestSceneDialogOverlay : OsuTestScene
{
- public TestSceneDialogOverlay()
+ private DialogOverlay overlay;
+
+ [SetUpSteps]
+ public void SetUpSteps()
{
- DialogOverlay overlay;
+ AddStep("create dialog overlay", () => Child = overlay = new DialogOverlay());
+ }
- Add(overlay = new DialogOverlay());
+ [Test]
+ public void TestBasic()
+ {
+ TestPopupDialog dialog = null;
- AddStep("dialog #1", () => overlay.Push(new TestPopupDialog
+ AddStep("dialog #1", () => overlay.Push(dialog = new TestPopupDialog
{
Icon = FontAwesome.Regular.TrashAlt,
HeaderText = @"Confirm deletion of",
@@ -37,7 +46,9 @@ namespace osu.Game.Tests.Visual.UserInterface
},
}));
- AddStep("dialog #2", () => overlay.Push(new TestPopupDialog
+ AddAssert("first dialog displayed", () => overlay.CurrentDialog == dialog);
+
+ AddStep("dialog #2", () => overlay.Push(dialog = new TestPopupDialog
{
Icon = FontAwesome.Solid.Cog,
HeaderText = @"What do you want to do with",
@@ -70,6 +81,42 @@ namespace osu.Game.Tests.Visual.UserInterface
},
},
}));
+
+ AddAssert("second dialog displayed", () => overlay.CurrentDialog == dialog);
+ }
+
+ [Test]
+ public void TestDismissBeforePush()
+ {
+ AddStep("dismissed dialog push", () =>
+ {
+ overlay.Push(new TestPopupDialog
+ {
+ State = { Value = Visibility.Hidden }
+ });
+ });
+
+ AddAssert("no dialog pushed", () => overlay.CurrentDialog == null);
+ }
+
+ [Test]
+ public void TestDismissBeforePushViaButtonPress()
+ {
+ AddStep("dismissed dialog push", () =>
+ {
+ TestPopupDialog dialog;
+ overlay.Push(dialog = new TestPopupDialog
+ {
+ Buttons = new PopupDialogButton[]
+ {
+ new PopupDialogOkButton { Text = @"OK" },
+ },
+ });
+
+ dialog.PerformOkAction();
+ });
+
+ AddAssert("no dialog pushed", () => overlay.CurrentDialog == null);
}
private class TestPopupDialog : PopupDialog
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 46e3a4f6d7..e7f6bb3c3a 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
@@ -244,6 +245,8 @@ namespace osu.Game.Beatmaps
{
var setInfo = info.BeatmapSet;
+ Debug.Assert(setInfo.Files != null);
+
using (var stream = new MemoryStream())
{
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
@@ -290,7 +293,9 @@ namespace osu.Game.Beatmaps
if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
return DefaultBeatmap;
- if (beatmapInfo.BeatmapSet.Files == null)
+ // force a re-query if files are not in a state which looks like the model has
+ // full database information present.
+ if (beatmapInfo.BeatmapSet.Files == null || beatmapInfo.BeatmapSet.Files.Count == 0)
{
var info = beatmapInfo;
beatmapInfo = QueryBeatmap(b => b.ID == info.ID);
diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs
index 1bcbe4dd2f..cd02900e88 100644
--- a/osu.Game/Overlays/Dialog/PopupDialog.cs
+++ b/osu.Game/Overlays/Dialog/PopupDialog.cs
@@ -95,6 +95,10 @@ namespace osu.Game.Overlays.Dialog
}
}
+ // We always want dialogs to show their appear animation, so we request they start hidden.
+ // Normally this would not be required, but is here due to the manual Show() call that occurs before LoadComplete().
+ protected override bool StartHidden => true;
+
protected PopupDialog()
{
RelativeSizeAxes = Axes.Both;
@@ -205,8 +209,17 @@ 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.
+ Show();
}
+ ///
+ /// Programmatically clicks the first .
+ ///
+ public void PerformOkAction() => Buttons.OfType().First().Click();
+
protected override bool OnKeyDown(KeyDownEvent e)
{
if (e.Repeat) return false;
diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs
index 4cc17a4c14..bc3b0e6c9a 100644
--- a/osu.Game/Overlays/DialogOverlay.cs
+++ b/osu.Game/Overlays/DialogOverlay.cs
@@ -35,15 +35,16 @@ namespace osu.Game.Overlays
public void Push(PopupDialog dialog)
{
- if (dialog == CurrentDialog) return;
+ if (dialog == CurrentDialog || dialog.State.Value != Visibility.Visible) return;
+ // if any existing dialog is being displayed, dismiss it before showing a new one.
CurrentDialog?.Hide();
+
CurrentDialog = dialog;
+ CurrentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue);
dialogContainer.Add(CurrentDialog);
- CurrentDialog.Show();
- CurrentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue);
Show();
}
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 5ac3401720..986a4efb28 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -473,25 +473,23 @@ namespace osu.Game.Screens.Edit
{
if (!exitConfirmed)
{
- // if the confirm dialog is already showing (or we can't show it, ie. in tests) exit without save.
- if (dialogOverlay == null || dialogOverlay.CurrentDialog is PromptForSaveDialog)
+ // dialog overlay may not be available in visual tests.
+ if (dialogOverlay == null)
{
confirmExit();
- return base.OnExiting(next);
+ return true;
+ }
+
+ // if the dialog is already displayed, confirm exit with no save.
+ if (dialogOverlay.CurrentDialog is PromptForSaveDialog saveDialog)
+ {
+ saveDialog.PerformOkAction();
+ return true;
}
if (isNewBeatmap || HasUnsavedChanges)
{
- dialogOverlay?.Push(new PromptForSaveDialog(() =>
- {
- confirmExit();
- this.Exit();
- }, () =>
- {
- confirmExitWithSave();
- this.Exit();
- }));
-
+ dialogOverlay?.Push(new PromptForSaveDialog(confirmExit, confirmExitWithSave));
return true;
}
}
@@ -499,15 +497,23 @@ namespace osu.Game.Screens.Edit
ApplyToBackground(b => b.FadeColour(Color4.White, 500));
resetTrack();
- Beatmap.Value = beatmapManager.GetWorkingBeatmap(Beatmap.Value.BeatmapInfo);
+ // To update the game-wide beatmap with any changes, perform a re-fetch on exit.
+ // This is required as the editor makes its local changes via EditorBeatmap
+ // (which are not propagated outwards to a potentially cached WorkingBeatmap).
+ var refetchedBeatmap = beatmapManager.GetWorkingBeatmap(Beatmap.Value.BeatmapInfo);
+
+ if (!(refetchedBeatmap is DummyWorkingBeatmap))
+ Beatmap.Value = refetchedBeatmap;
return base.OnExiting(next);
}
private void confirmExitWithSave()
{
- exitConfirmed = true;
Save();
+
+ exitConfirmed = true;
+ this.Exit();
}
private void confirmExit()
@@ -529,6 +535,7 @@ namespace osu.Game.Screens.Edit
}
exitConfirmed = true;
+ this.Exit();
}
private readonly Bindable clipboard = new Bindable();
diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs
index baeb86c976..e53b46f391 100644
--- a/osu.Game/Screens/Menu/MainMenu.cs
+++ b/osu.Game/Screens/Menu/MainMenu.cs
@@ -1,9 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Linq;
-using osuTK;
-using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -22,6 +19,8 @@ using osu.Game.Screens.Edit;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Screens.Select;
+using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Screens.Menu
{
@@ -120,7 +119,7 @@ namespace osu.Game.Screens.Menu
Origin = Anchor.TopRight,
Margin = new MarginPadding { Right = 15, Top = 5 }
},
- exitConfirmOverlay?.CreateProxy() ?? Drawable.Empty()
+ exitConfirmOverlay?.CreateProxy() ?? Empty()
});
buttons.StateChanged += state =>
@@ -270,15 +269,11 @@ namespace osu.Game.Screens.Menu
if (!exitConfirmed && dialogOverlay != null)
{
if (dialogOverlay.CurrentDialog is ConfirmExitDialog exitDialog)
- {
- exitConfirmed = true;
- exitDialog.Buttons.First().Click();
- }
+ exitDialog.PerformOkAction();
else
- {
dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort()));
- return true;
- }
+
+ return true;
}
buttons.State = ButtonSystemState.Exit;