Merge pull request #18111 from bdach/mod-overlay/integration

Replace old mod overlay with new design
This commit is contained in:
Dean Herbert 2022-05-08 02:44:50 +09:00 committed by GitHub
commit 3bb22dece6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 353 additions and 113 deletions

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Linq;
using System.Threading; using System.Threading;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -283,7 +282,7 @@ namespace osu.Game.Tests.Visual.Background
AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmapInfo != null); AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmapInfo != null);
AddStep("Set default user settings", () => AddStep("Set default user settings", () =>
{ {
SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray(); SelectedMods.Value = new[] { new OsuModNoFail() };
songSelect.DimLevel.Value = 0.7f; songSelect.DimLevel.Value = 0.7f;
songSelect.BlurLevel.Value = 0.4f; songSelect.BlurLevel.Value = 0.4f;
}); });

View File

@ -627,7 +627,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("invoke on back button", () => multiplayerComponents.OnBackButton()); AddStep("invoke on back button", () => multiplayerComponents.OnBackButton());
AddAssert("mod overlay is hidden", () => this.ChildrenOfType<UserModSelectOverlay>().Single().State.Value == Visibility.Hidden); AddAssert("mod overlay is hidden", () => this.ChildrenOfType<UserModSelectScreen>().Single().State.Value == Visibility.Hidden);
AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden); AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden);

View File

@ -132,7 +132,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void assertHasFreeModButton(Type type, bool hasButton = true) private void assertHasFreeModButton(Type type, bool hasButton = true)
{ {
AddAssert($"{type.ReadableName()} {(hasButton ? "displayed" : "not displayed")} in freemod overlay", AddAssert($"{type.ReadableName()} {(hasButton ? "displayed" : "not displayed")} in freemod overlay",
() => songSelect.ChildrenOfType<FreeModSelectOverlay>().Single().ChildrenOfType<ModButton>().All(b => b.Mod.GetType() != type)); () => this.ChildrenOfType<FreeModSelectScreen>()
.Single()
.ChildrenOfType<ModPanel>()
.Where(panel => !panel.Filtered.Value)
.All(b => b.Mod.GetType() != type));
} }
private class TestMultiplayerMatchSongSelect : MultiplayerMatchSongSelect private class TestMultiplayerMatchSongSelect : MultiplayerMatchSongSelect

View File

@ -168,8 +168,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
ClickButtonWhenEnabled<RoomSubScreen.UserModSelectButton>(); ClickButtonWhenEnabled<RoomSubScreen.UserModSelectButton>();
AddUntilStep("mod select contents loaded",
() => this.ChildrenOfType<ModColumn>().Any() && this.ChildrenOfType<ModColumn>().All(col => col.IsLoaded && col.ItemsLoaded));
AddUntilStep("mod select contains only double time mod", AddUntilStep("mod select contains only double time mod",
() => this.ChildrenOfType<UserModSelectOverlay>().SingleOrDefault()?.ChildrenOfType<ModButton>().SingleOrDefault()?.Mod is OsuModDoubleTime); () => this.ChildrenOfType<UserModSelectScreen>()
.SingleOrDefault()?
.ChildrenOfType<ModPanel>()
.SingleOrDefault(panel => !panel.Filtered.Value)?.Mod is OsuModDoubleTime);
} }
} }
} }

View File

@ -253,12 +253,12 @@ namespace osu.Game.Tests.Visual.Navigation
PushAndConfirm(() => songSelect = new TestPlaySongSelect()); PushAndConfirm(() => songSelect = new TestPlaySongSelect());
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show()); AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible); AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition));
// BackButton handles hover using its child button, so this checks whether or not any of BackButton's children are hovered. AddStep("Move mouse to dimmed area", () => InputManager.MoveMouseTo(new Vector2(
AddUntilStep("Back button is hovered", () => Game.ChildrenOfType<BackButton>().First().Children.Any(c => c.IsHovered)); songSelect.ScreenSpaceDrawQuad.TopLeft.X + 1,
songSelect.ScreenSpaceDrawQuad.TopLeft.Y + songSelect.ScreenSpaceDrawQuad.Height / 2)));
AddStep("Click left mouse button", () => InputManager.Click(MouseButton.Left));
AddStep("Click back button", () => InputManager.Click(MouseButton.Left));
AddUntilStep("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden); AddUntilStep("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
exitViaBackButtonAndConfirm(); exitViaBackButtonAndConfirm();
} }
@ -551,7 +551,7 @@ namespace osu.Game.Tests.Visual.Navigation
public class TestPlaySongSelect : PlaySongSelect public class TestPlaySongSelect : PlaySongSelect
{ {
public ModSelectOverlay ModSelectOverlay => ModSelect; public ModSelectScreen ModSelectOverlay => ModSelect;
public BeatmapOptionsOverlay BeatmapOptionsOverlay => BeatmapOptions; public BeatmapOptionsOverlay BeatmapOptionsOverlay => BeatmapOptions;

View File

@ -8,6 +8,7 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing; using osu.Framework.Testing;
@ -18,6 +19,7 @@ using osu.Game.Extensions;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
@ -918,6 +920,19 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("check ruleset is correct for score", () => Ruleset.Value.OnlineID == 0); AddAssert("check ruleset is correct for score", () => Ruleset.Value.OnlineID == 0);
} }
[Test]
public void TestModOverlayToggling()
{
changeRuleset(0);
createSongSelect();
AddStep("toggle mod overlay on", () => InputManager.Key(Key.F1));
AddUntilStep("mod overlay shown", () => songSelect.ModSelect.State.Value == Visibility.Visible);
AddStep("toggle mod overlay off", () => InputManager.Key(Key.F1));
AddUntilStep("mod overlay hidden", () => songSelect.ModSelect.State.Value == Visibility.Hidden);
}
private void waitForInitialSelection() private void waitForInitialSelection()
{ {
AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault); AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
@ -993,6 +1008,7 @@ namespace osu.Game.Tests.Visual.SongSelect
public WorkingBeatmap CurrentBeatmap => Beatmap.Value; public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
public IWorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public IWorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap;
public new BeatmapCarousel Carousel => base.Carousel; public new BeatmapCarousel Carousel => base.Carousel;
public new ModSelectScreen ModSelect => base.ModSelect;
public new void PresentScore(ScoreInfo score) => base.PresentScore(score); public new void PresentScore(ScoreInfo score) => base.PresentScore(score);

View File

@ -66,7 +66,10 @@ namespace osu.Game.Tests.Visual.UserInterface
public class TestShearedOverlayContainer : ShearedOverlayContainer public class TestShearedOverlayContainer : ShearedOverlayContainer
{ {
protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Green; public TestShearedOverlayContainer()
: base(OverlayColourScheme.Green)
{
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()

View File

@ -34,7 +34,7 @@ namespace osu.Game.Graphics.Containers
protected virtual bool DimMainContent => true; protected virtual bool DimMainContent => true;
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private OsuGame game { get; set; } private IOverlayManager overlayManager { get; set; }
[Resolved] [Resolved]
private PreviewTrackManager previewTrackManager { get; set; } private PreviewTrackManager previewTrackManager { get; set; }
@ -50,8 +50,8 @@ namespace osu.Game.Graphics.Containers
protected override void LoadComplete() protected override void LoadComplete()
{ {
if (game != null) if (overlayManager != null)
OverlayActivationMode.BindTo(game.OverlayActivationMode); OverlayActivationMode.BindTo(overlayManager.OverlayActivationMode);
OverlayActivationMode.BindValueChanged(mode => OverlayActivationMode.BindValueChanged(mode =>
{ {
@ -127,14 +127,14 @@ namespace osu.Game.Graphics.Containers
if (didChange) if (didChange)
samplePopIn?.Play(); samplePopIn?.Play();
if (BlockScreenWideMouse && DimMainContent) game?.AddBlockingOverlay(this); if (BlockScreenWideMouse && DimMainContent) overlayManager?.ShowBlockingOverlay(this);
break; break;
case Visibility.Hidden: case Visibility.Hidden:
if (didChange) if (didChange)
samplePopOut?.Play(); samplePopOut?.Play();
if (BlockScreenWideMouse) game?.RemoveBlockingOverlay(this); if (BlockScreenWideMouse) overlayManager?.HideBlockingOverlay(this);
break; break;
} }
@ -150,7 +150,7 @@ namespace osu.Game.Graphics.Containers
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);
game?.RemoveBlockingOverlay(this); overlayManager?.HideBlockingOverlay(this);
} }
} }
} }

View File

@ -63,7 +63,7 @@ namespace osu.Game
/// The full osu! experience. Builds on top of <see cref="OsuGameBase"/> to add menus and binding logic /// The full osu! experience. Builds on top of <see cref="OsuGameBase"/> to add menus and binding logic
/// for initial components that are generally retrieved via DI. /// for initial components that are generally retrieved via DI.
/// </summary> /// </summary>
public class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>, ILocalUserPlayInfo, IPerformFromScreenRunner public class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>, ILocalUserPlayInfo, IPerformFromScreenRunner, IOverlayManager
{ {
/// <summary> /// <summary>
/// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications). /// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications).
@ -171,6 +171,7 @@ namespace osu.Game
private readonly string[] args; private readonly string[] args;
private readonly List<OsuFocusedOverlayContainer> focusedOverlays = new List<OsuFocusedOverlayContainer>(); private readonly List<OsuFocusedOverlayContainer> focusedOverlays = new List<OsuFocusedOverlayContainer>();
private readonly List<OverlayContainer> externalOverlays = new List<OverlayContainer>();
private readonly List<OverlayContainer> visibleBlockingOverlays = new List<OverlayContainer>(); private readonly List<OverlayContainer> visibleBlockingOverlays = new List<OverlayContainer>();
@ -183,22 +184,50 @@ namespace osu.Game
SentryLogger = new SentryLogger(this); SentryLogger = new SentryLogger(this);
} }
#region IOverlayManager
IBindable<OverlayActivation> IOverlayManager.OverlayActivationMode => OverlayActivationMode;
private void updateBlockingOverlayFade() => private void updateBlockingOverlayFade() =>
ScreenContainer.FadeColour(visibleBlockingOverlays.Any() ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint); ScreenContainer.FadeColour(visibleBlockingOverlays.Any() ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint);
public void AddBlockingOverlay(OverlayContainer overlay) IDisposable IOverlayManager.RegisterBlockingOverlay(OverlayContainer overlayContainer)
{
if (overlayContainer.Parent != null)
throw new ArgumentException($@"Overlays registered via {nameof(IOverlayManager.RegisterBlockingOverlay)} should not be added to the scene graph.");
if (externalOverlays.Contains(overlayContainer))
throw new ArgumentException($@"{overlayContainer} has already been registered via {nameof(IOverlayManager.RegisterBlockingOverlay)} once.");
externalOverlays.Add(overlayContainer);
overlayContent.Add(overlayContainer);
return new InvokeOnDisposal(() => unregisterBlockingOverlay(overlayContainer));
}
void IOverlayManager.ShowBlockingOverlay(OverlayContainer overlay)
{ {
if (!visibleBlockingOverlays.Contains(overlay)) if (!visibleBlockingOverlays.Contains(overlay))
visibleBlockingOverlays.Add(overlay); visibleBlockingOverlays.Add(overlay);
updateBlockingOverlayFade(); updateBlockingOverlayFade();
} }
public void RemoveBlockingOverlay(OverlayContainer overlay) => Schedule(() => void IOverlayManager.HideBlockingOverlay(OverlayContainer overlay) => Schedule(() =>
{ {
visibleBlockingOverlays.Remove(overlay); visibleBlockingOverlays.Remove(overlay);
updateBlockingOverlayFade(); updateBlockingOverlayFade();
}); });
/// <summary>
/// Unregisters a blocking <see cref="OverlayContainer"/> that was not created by <see cref="OsuGame"/> itself.
/// </summary>
private void unregisterBlockingOverlay(OverlayContainer overlayContainer)
{
externalOverlays.Remove(overlayContainer);
overlayContainer.Expire();
}
#endregion
/// <summary> /// <summary>
/// Close all game-wide overlays. /// Close all game-wide overlays.
/// </summary> /// </summary>

View File

@ -31,8 +31,6 @@ namespace osu.Game.Overlays
[Cached] [Cached]
public class FirstRunSetupOverlay : ShearedOverlayContainer public class FirstRunSetupOverlay : ShearedOverlayContainer
{ {
protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Purple;
[Resolved] [Resolved]
private IPerformFromScreenRunner performer { get; set; } = null!; private IPerformFromScreenRunner performer { get; set; } = null!;
@ -70,6 +68,11 @@ namespace osu.Game.Overlays
private Container content = null!; private Container content = null!;
public FirstRunSetupOverlay()
: base(OverlayColourScheme.Purple)
{
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {

View File

@ -0,0 +1,44 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Screens.Select;
namespace osu.Game.Overlays
{
[Cached]
internal interface IOverlayManager
{
/// <summary>
/// Whether overlays should be able to be opened game-wide. Value is sourced from the current active screen.
/// </summary>
IBindable<OverlayActivation> OverlayActivationMode { get; }
/// <summary>
/// Registers a blocking <see cref="OverlayContainer"/> that was not created by <see cref="OsuGame"/> itself for later use.
/// </summary>
/// <remarks>
/// The goal of this method is to allow child screens, like <see cref="SongSelect"/> to register their own full-screen blocking overlays
/// with background dim.
/// In those cases, for the dim to work correctly, the overlays need to be added at a game level directly, rather as children of the screens.
/// </remarks>
/// <returns>
/// An <see cref="IDisposable"/> that should be disposed of when the <paramref name="overlayContainer"/> should be unregistered.
/// Disposing of this <see cref="IDisposable"/> will automatically expire the <paramref name="overlayContainer"/>.
/// </returns>
IDisposable RegisterBlockingOverlay(OverlayContainer overlayContainer);
/// <summary>
/// Should be called when <paramref name="overlay"/> has been shown and should begin blocking background input.
/// </summary>
void ShowBlockingOverlay(OverlayContainer overlay);
/// <summary>
/// Should be called when a blocking <paramref name="overlay"/> has been hidden and should stop blocking background input.
/// </summary>
void HideBlockingOverlay(OverlayContainer overlay);
}
}

View File

@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Mods
private Func<Mod, bool>? filter; private Func<Mod, bool>? filter;
/// <summary> /// <summary>
/// Function determining whether each mod in the column should be displayed. /// A function determining whether each mod in the column should be displayed.
/// A return value of <see langword="true"/> means that the mod is not filtered and therefore its corresponding panel should be displayed. /// A return value of <see langword="true"/> means that the mod is not filtered and therefore its corresponding panel should be displayed.
/// A return value of <see langword="false"/> means that the mod is filtered out and therefore its corresponding panel should be hidden. /// A return value of <see langword="false"/> means that the mod is filtered out and therefore its corresponding panel should be hidden.
/// </summary> /// </summary>
@ -250,9 +250,8 @@ namespace osu.Game.Overlays.Mods
private void load(OsuGameBase game, OverlayColourProvider colourProvider, OsuColour colours) private void load(OsuGameBase game, OverlayColourProvider colourProvider, OsuColour colours)
{ {
availableMods.BindTo(game.AvailableMods); availableMods.BindTo(game.AvailableMods);
// this `BindValueChanged` callback is intentionally here, to ensure that local available mods are constructed as early as possible. updateLocalAvailableMods(asyncLoadContent: false);
// this is needed to make sure no external changes to mods are dropped while mod panels are asynchronously loading. availableMods.BindValueChanged(_ => updateLocalAvailableMods(asyncLoadContent: true));
availableMods.BindValueChanged(_ => updateLocalAvailableMods(), true);
headerBackground.Colour = accentColour = colours.ForModType(ModType); headerBackground.Colour = accentColour = colours.ForModType(ModType);
@ -279,7 +278,7 @@ namespace osu.Game.Overlays.Mods
toggleAllCheckbox.LabelText = toggleAllCheckbox.Current.Value ? CommonStrings.DeselectAll : CommonStrings.SelectAll; toggleAllCheckbox.LabelText = toggleAllCheckbox.Current.Value ? CommonStrings.DeselectAll : CommonStrings.SelectAll;
} }
private void updateLocalAvailableMods() private void updateLocalAvailableMods(bool asyncLoadContent)
{ {
var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty<Mod>()) var newMods = ModUtils.FlattenMods(availableMods.Value.GetValueOrDefault(ModType) ?? Array.Empty<Mod>())
.Select(m => m.DeepClone()) .Select(m => m.DeepClone())
@ -290,11 +289,10 @@ namespace osu.Game.Overlays.Mods
localAvailableMods = newMods; localAvailableMods = newMods;
if (!IsLoaded) if (asyncLoadContent)
// if we're coming from BDL, perform the first load synchronously to make sure everything is in place as early as possible.
onPanelsLoaded(createPanels());
else
asyncLoadPanels(); asyncLoadPanels();
else
onPanelsLoaded(createPanels());
} }
private CancellationTokenSource? cancellationTokenSource; private CancellationTokenSource? cancellationTokenSource;

View File

@ -20,10 +20,10 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Localisation;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osuTK; using osuTK;
using osuTK.Input; using osuTK.Input;
using osu.Game.Localisation;
namespace osu.Game.Overlays.Mods namespace osu.Game.Overlays.Mods
{ {
@ -31,13 +31,16 @@ namespace osu.Game.Overlays.Mods
{ {
protected const int BUTTON_WIDTH = 200; protected const int BUTTON_WIDTH = 200;
protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Green;
[Cached] [Cached]
public Bindable<IReadOnlyList<Mod>> SelectedMods { get; private set; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>()); public Bindable<IReadOnlyList<Mod>> SelectedMods { get; private set; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
private Func<Mod, bool> isValidMod = m => true; private Func<Mod, bool> isValidMod = m => true;
/// <summary>
/// A function determining whether each mod in the column should be displayed.
/// A return value of <see langword="true"/> means that the mod is not filtered and therefore its corresponding panel should be displayed.
/// A return value of <see langword="false"/> means that the mod is filtered out and therefore its corresponding panel should be hidden.
/// </summary>
public Func<Mod, bool> IsValidMod public Func<Mod, bool> IsValidMod
{ {
get => isValidMod; get => isValidMod;
@ -57,31 +60,27 @@ namespace osu.Game.Overlays.Mods
protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys); protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys);
protected virtual IEnumerable<ShearedButton> CreateFooterButtons() => new[] protected virtual IReadOnlyList<Mod> ComputeNewModsFromSelection(IReadOnlyList<Mod> oldSelection, IReadOnlyList<Mod> newSelection) => newSelection;
{
customisationButton = new ShearedToggleButton(BUTTON_WIDTH) protected virtual IEnumerable<ShearedButton> CreateFooterButtons() => createDefaultFooterButtons();
{
Text = ModSelectScreenStrings.ModCustomisation,
Active = { BindTarget = customisationVisible }
},
new ShearedButton(BUTTON_WIDTH)
{
Text = CommonStrings.DeselectAll,
Action = DeselectAll
}
};
private readonly BindableBool customisationVisible = new BindableBool(); private readonly BindableBool customisationVisible = new BindableBool();
private DifficultyMultiplierDisplay? multiplierDisplay;
private ModSettingsArea modSettingsArea = null!; private ModSettingsArea modSettingsArea = null!;
private ColumnScrollContainer columnScroll = null!; private ColumnScrollContainer columnScroll = null!;
private ColumnFlowContainer columnFlow = null!; private ColumnFlowContainer columnFlow = null!;
private FillFlowContainer<ShearedButton> footerButtonFlow = null!; private FillFlowContainer<ShearedButton> footerButtonFlow = null!;
private ShearedButton backButton = null!; private ShearedButton backButton = null!;
private DifficultyMultiplierDisplay? multiplierDisplay;
private ShearedToggleButton? customisationButton; private ShearedToggleButton? customisationButton;
protected ModSelectScreen(OverlayColourScheme colourScheme = OverlayColourScheme.Green)
: base(colourScheme)
{
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
@ -185,14 +184,6 @@ namespace osu.Game.Overlays.Mods
}; };
} }
private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null)
=> new ColumnDimContainer(CreateModColumn(modType, toggleKeys))
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
RequestScroll = column => columnScroll.ScrollIntoView(column, extraScroll: 140)
};
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
@ -216,6 +207,47 @@ namespace osu.Game.Overlays.Mods
updateAvailableMods(); updateAvailableMods();
} }
/// <summary>
/// Select all visible mods in all columns.
/// </summary>
protected void SelectAll()
{
foreach (var column in columnFlow.Columns)
column.SelectAll();
}
/// <summary>
/// Deselect all visible mods in all columns.
/// </summary>
protected void DeselectAll()
{
foreach (var column in columnFlow.Columns)
column.DeselectAll();
}
private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null)
=> new ColumnDimContainer(CreateModColumn(modType, toggleKeys))
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
RequestScroll = column => columnScroll.ScrollIntoView(column, extraScroll: 140)
};
private ShearedButton[] createDefaultFooterButtons()
=> new[]
{
customisationButton = new ShearedToggleButton(BUTTON_WIDTH)
{
Text = ModSelectScreenStrings.ModCustomisation,
Active = { BindTarget = customisationVisible }
},
new ShearedButton(BUTTON_WIDTH)
{
Text = CommonStrings.DeselectAll,
Action = DeselectAll
}
};
private void updateMultiplier() private void updateMultiplier()
{ {
if (multiplierDisplay == null) if (multiplierDisplay == null)
@ -306,7 +338,7 @@ namespace osu.Game.Overlays.Mods
SelectedMods.Value = ComputeNewModsFromSelection(SelectedMods.Value, candidateSelection); SelectedMods.Value = ComputeNewModsFromSelection(SelectedMods.Value, candidateSelection);
} }
protected virtual IReadOnlyList<Mod> ComputeNewModsFromSelection(IReadOnlyList<Mod> oldSelection, IReadOnlyList<Mod> newSelection) => newSelection; #region Transition handling
protected override void PopIn() protected override void PopIn()
{ {
@ -352,20 +384,17 @@ namespace osu.Game.Overlays.Mods
} }
} }
protected void SelectAll() #endregion
{
foreach (var column in columnFlow.Columns)
column.SelectAll();
}
protected void DeselectAll() #region Input handling
{
foreach (var column in columnFlow.Columns)
column.DeselectAll();
}
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e) public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{ {
if (e.Repeat)
return false;
// This is handled locally here because this overlay is being registered at the game level
// and therefore takes away keyboard focus from the screen stack.
if (e.Action == GlobalAction.Back) if (e.Action == GlobalAction.Back)
{ {
if (customisationVisible.Value) if (customisationVisible.Value)
@ -375,9 +404,27 @@ namespace osu.Game.Overlays.Mods
return true; return true;
} }
return base.OnPressed(e); switch (e.Action)
{
case GlobalAction.ToggleModSelection:
case GlobalAction.Select:
{
if (customisationVisible.Value)
customisationVisible.Value = false;
Hide();
return true;
} }
default:
return base.OnPressed(e);
}
}
#endregion
/// <summary>
/// Manages horizontal scrolling of mod columns, along with the "active" states of each column based on visibility.
/// </summary>
internal class ColumnScrollContainer : OsuScrollContainer<ColumnFlowContainer> internal class ColumnScrollContainer : OsuScrollContainer<ColumnFlowContainer>
{ {
public ColumnScrollContainer() public ColumnScrollContainer()
@ -416,6 +463,9 @@ namespace osu.Game.Overlays.Mods
} }
} }
/// <summary>
/// Manages padding and layout of mod columns.
/// </summary>
internal class ColumnFlowContainer : FillFlowContainer<ColumnDimContainer> internal class ColumnFlowContainer : FillFlowContainer<ColumnDimContainer>
{ {
public IEnumerable<ModColumn> Columns => Children.Select(dimWrapper => dimWrapper.Column); public IEnumerable<ModColumn> Columns => Children.Select(dimWrapper => dimWrapper.Column);
@ -452,11 +502,21 @@ namespace osu.Game.Overlays.Mods
} }
} }
/// <summary>
/// Encapsulates a column and provides dim and input blocking based on an externally managed "active" state.
/// </summary>
internal class ColumnDimContainer : Container internal class ColumnDimContainer : Container
{ {
public ModColumn Column { get; } public ModColumn Column { get; }
/// <summary>
/// Tracks whether this column is in an interactive state. Generally only the case when the column is on-screen.
/// </summary>
public readonly Bindable<bool> Active = new BindableBool(); public readonly Bindable<bool> Active = new BindableBool();
/// <summary>
/// Invoked when the column is clicked while not active, requesting a scroll to be performed to bring it on-screen.
/// </summary>
public Action<ColumnDimContainer>? RequestScroll { get; set; } public Action<ColumnDimContainer>? RequestScroll { get; set; }
[Resolved] [Resolved]
@ -511,6 +571,9 @@ namespace osu.Game.Overlays.Mods
} }
} }
/// <summary>
/// A container which blocks and handles input, managing the "return from customisation" state change.
/// </summary>
private class ClickToReturnContainer : Container private class ClickToReturnContainer : Container
{ {
public BindableBool HandleMouse { get; } = new BindableBool(); public BindableBool HandleMouse { get; } = new BindableBool();

View File

@ -51,17 +51,15 @@ namespace osu.Game.Overlays.Mods
/// </summary> /// </summary>
protected Container FooterContent { get; private set; } protected Container FooterContent { get; private set; }
protected abstract OverlayColourScheme ColourScheme { get; }
protected override bool StartHidden => true; protected override bool StartHidden => true;
protected override bool BlockNonPositionalInput => true; protected override bool BlockNonPositionalInput => true;
protected ShearedOverlayContainer() protected ShearedOverlayContainer(OverlayColourScheme colourScheme)
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
ColourProvider = new OverlayColourProvider(ColourScheme); ColourProvider = new OverlayColourProvider(colourScheme);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -12,6 +12,11 @@ namespace osu.Game.Overlays.Mods
{ {
public class UserModSelectScreen : ModSelectScreen public class UserModSelectScreen : ModSelectScreen
{ {
public UserModSelectScreen(OverlayColourScheme colourScheme = OverlayColourScheme.Green)
: base(colourScheme)
{
}
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys); protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys);
protected override IReadOnlyList<Mod> ComputeNewModsFromSelection(IReadOnlyList<Mod> oldSelection, IReadOnlyList<Mod> newSelection) protected override IReadOnlyList<Mod> ComputeNewModsFromSelection(IReadOnlyList<Mod> oldSelection, IReadOnlyList<Mod> newSelection)

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using osu.Game.Overlays;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -23,6 +24,7 @@ namespace osu.Game.Screens.OnlinePlay
} }
public FreeModSelectScreen() public FreeModSelectScreen()
: base(OverlayColourScheme.Plum)
{ {
IsValidMod = _ => true; IsValidMod = _ => true;
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
@ -57,6 +58,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
protected readonly IBindable<long?> RoomId = new Bindable<long?>(); protected readonly IBindable<long?> RoomId = new Bindable<long?>();
[Resolved(CanBeNull = true)]
private IOverlayManager overlayManager { get; set; }
[Resolved] [Resolved]
private MusicController music { get; set; } private MusicController music { get; set; }
@ -77,7 +81,11 @@ namespace osu.Game.Screens.OnlinePlay.Match
public readonly Room Room; public readonly Room Room;
private readonly bool allowEdit; private readonly bool allowEdit;
private ModSelectOverlay userModsSelectOverlay; private ModSelectScreen userModsSelectOverlay;
[CanBeNull]
private IDisposable userModsSelectOverlayRegistration;
private RoomSettingsOverlay settingsOverlay; private RoomSettingsOverlay settingsOverlay;
private Drawable mainContent; private Drawable mainContent;
@ -180,11 +188,6 @@ namespace osu.Game.Screens.OnlinePlay.Match
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Child = userModsSelectOverlay = new UserModSelectOverlay
{
SelectedMods = { BindTarget = UserMods },
IsValidMod = _ => false
}
}, },
} }
} }
@ -227,6 +230,12 @@ namespace osu.Game.Screens.OnlinePlay.Match
} }
} }
}; };
LoadComponent(userModsSelectOverlay = new UserModSelectScreen(OverlayColourScheme.Plum)
{
SelectedMods = { BindTarget = UserMods },
IsValidMod = _ => false
});
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -254,6 +263,8 @@ namespace osu.Game.Screens.OnlinePlay.Match
beatmapAvailabilityTracker.SelectedItem.BindTo(SelectedItem); beatmapAvailabilityTracker.SelectedItem.BindTo(SelectedItem);
beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateWorkingBeatmap()); beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateWorkingBeatmap());
userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(userModsSelectOverlay);
} }
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
@ -298,7 +309,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
public override void OnSuspending(ScreenTransitionEvent e) public override void OnSuspending(ScreenTransitionEvent e)
{ {
endHandlingTrack(); onLeaving();
base.OnSuspending(e); base.OnSuspending(e);
} }
@ -316,7 +327,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
RoomManager?.PartRoom(); RoomManager?.PartRoom();
Mods.Value = Array.Empty<Mod>(); Mods.Value = Array.Empty<Mod>();
endHandlingTrack(); onLeaving();
return base.OnExiting(e); return base.OnExiting(e);
} }
@ -412,6 +423,12 @@ namespace osu.Game.Screens.OnlinePlay.Match
Beatmap.BindValueChanged(applyLoopingToTrack, true); Beatmap.BindValueChanged(applyLoopingToTrack, true);
} }
private void onLeaving()
{
userModsSelectOverlay.Hide();
endHandlingTrack();
}
private void endHandlingTrack() private void endHandlingTrack()
{ {
Beatmap.ValueChanged -= applyLoopingToTrack; Beatmap.ValueChanged -= applyLoopingToTrack;
@ -459,5 +476,12 @@ namespace osu.Game.Screens.OnlinePlay.Match
public class UserModSelectButton : PurpleTriangleButton public class UserModSelectButton : PurpleTriangleButton
{ {
} }
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
userModsSelectOverlayRegistration?.Dispose();
}
} }
} }

View File

@ -14,6 +14,7 @@ using osu.Framework.Screens;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods; using osu.Game.Overlays.Mods;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
@ -45,7 +46,6 @@ namespace osu.Game.Screens.OnlinePlay
protected readonly Bindable<IReadOnlyList<Mod>> FreeMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>()); protected readonly Bindable<IReadOnlyList<Mod>> FreeMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
private readonly FreeModSelectOverlay freeModSelectOverlay;
private readonly Room room; private readonly Room room;
private WorkingBeatmap initialBeatmap; private WorkingBeatmap initialBeatmap;
@ -53,13 +53,16 @@ namespace osu.Game.Screens.OnlinePlay
private IReadOnlyList<Mod> initialMods; private IReadOnlyList<Mod> initialMods;
private bool itemSelected; private bool itemSelected;
private readonly FreeModSelectScreen freeModSelectOverlay;
private IDisposable freeModSelectOverlayRegistration;
protected OnlinePlaySongSelect(Room room) protected OnlinePlaySongSelect(Room room)
{ {
this.room = room; this.room = room;
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING };
freeModSelectOverlay = new FreeModSelectOverlay freeModSelectOverlay = new FreeModSelectScreen
{ {
SelectedMods = { BindTarget = FreeMods }, SelectedMods = { BindTarget = FreeMods },
IsValidMod = IsValidFreeMod, IsValidMod = IsValidFreeMod,
@ -75,7 +78,7 @@ namespace osu.Game.Screens.OnlinePlay
initialRuleset = Ruleset.Value; initialRuleset = Ruleset.Value;
initialMods = Mods.Value.ToList(); initialMods = Mods.Value.ToList();
FooterPanels.Add(freeModSelectOverlay); LoadComponent(freeModSelectOverlay);
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -94,6 +97,8 @@ namespace osu.Game.Screens.OnlinePlay
Mods.BindValueChanged(onModsChanged); Mods.BindValueChanged(onModsChanged);
Ruleset.BindValueChanged(onRulesetChanged); Ruleset.BindValueChanged(onRulesetChanged);
freeModSelectOverlayRegistration = OverlayManager?.RegisterBlockingOverlay(freeModSelectOverlay);
} }
private void onModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods) private void onModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
@ -150,10 +155,12 @@ namespace osu.Game.Screens.OnlinePlay
Mods.Value = initialMods; Mods.Value = initialMods;
} }
freeModSelectOverlay.Hide();
return base.OnExiting(e); return base.OnExiting(e);
} }
protected override ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay protected override ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen(OverlayColourScheme.Plum)
{ {
IsValidMod = IsValidMod IsValidMod = IsValidMod
}; };
@ -182,5 +189,12 @@ namespace osu.Game.Screens.OnlinePlay
private bool checkCompatibleFreeMod(Mod mod) private bool checkCompatibleFreeMod(Mod mod)
=> Mods.Value.All(m => m.Acronym != mod.Acronym) // Mod must not be contained in the required mods. => Mods.Value.All(m => m.Acronym != mod.Acronym) // Mod must not be contained in the required mods.
&& ModUtils.CheckCompatibleSet(Mods.Value.Append(mod).ToArray()); // Mod must be compatible with all the required mods. && ModUtils.CheckCompatibleSet(Mods.Value.Append(mod).ToArray()); // Mod must be compatible with all the required mods.
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
freeModSelectOverlayRegistration?.Dispose();
}
} }
} }

View File

@ -35,6 +35,7 @@ using osu.Framework.Input.Bindings;
using osu.Game.Collections; using osu.Game.Collections;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using System.Diagnostics; using System.Diagnostics;
using JetBrains.Annotations;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -101,7 +102,7 @@ namespace osu.Game.Screens.Select
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private LegacyImportManager legacyImportManager { get; set; } private LegacyImportManager legacyImportManager { get; set; }
protected ModSelectOverlay ModSelect { get; private set; } protected ModSelectScreen ModSelect { get; private set; }
protected Sample SampleConfirm { get; private set; } protected Sample SampleConfirm { get; private set; }
@ -116,9 +117,15 @@ namespace osu.Game.Screens.Select
private double audioFeedbackLastPlaybackTime; private double audioFeedbackLastPlaybackTime;
[CanBeNull]
private IDisposable modSelectOverlayRegistration;
[Resolved] [Resolved]
private MusicController music { get; set; } private MusicController music { get; set; }
[Resolved(CanBeNull = true)]
internal IOverlayManager OverlayManager { get; private set; }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(AudioManager audio, IDialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender) private void load(AudioManager audio, IDialogOverlay dialog, OsuColour colours, ManageCollectionsDialog manageCollectionsDialog, DifficultyRecommender recommender)
{ {
@ -251,19 +258,6 @@ namespace osu.Game.Screens.Select
if (ShowFooter) if (ShowFooter)
{ {
AddRangeInternal(new Drawable[] AddRangeInternal(new Drawable[]
{
new GridContainer // used for max height implementation
{
RelativeSizeAxes = Axes.Both,
RowDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.Relative, 1f, maxSize: ModSelectOverlay.HEIGHT + Footer.HEIGHT),
},
Content = new[]
{
null,
new Drawable[]
{ {
FooterPanels = new Container FooterPanels = new Container
{ {
@ -274,16 +268,16 @@ namespace osu.Game.Screens.Select
Children = new Drawable[] Children = new Drawable[]
{ {
BeatmapOptions = new BeatmapOptionsOverlay(), BeatmapOptions = new BeatmapOptionsOverlay(),
ModSelect = CreateModSelectOverlay()
}
}
}
} }
}, },
Footer = new Footer() Footer = new Footer(),
}); });
} }
// preload the mod select overlay for later use in `LoadComplete()`.
// therein it will be registered at the `OsuGame` level to properly function as a blocking overlay.
LoadComponent(ModSelect = CreateModSelectOverlay());
if (Footer != null) if (Footer != null)
{ {
foreach (var (button, overlay) in CreateFooterButtons()) foreach (var (button, overlay) in CreateFooterButtons())
@ -317,6 +311,13 @@ namespace osu.Game.Screens.Select
} }
} }
protected override void LoadComplete()
{
base.LoadComplete();
modSelectOverlayRegistration = OverlayManager?.RegisterBlockingOverlay(ModSelect);
}
/// <summary> /// <summary>
/// Creates the buttons to be displayed in the footer. /// Creates the buttons to be displayed in the footer.
/// </summary> /// </summary>
@ -332,7 +333,7 @@ namespace osu.Game.Screens.Select
(new FooterButtonOptions(), BeatmapOptions) (new FooterButtonOptions(), BeatmapOptions)
}; };
protected virtual ModSelectOverlay CreateModSelectOverlay() => new UserModSelectOverlay(); protected virtual ModSelectScreen CreateModSelectOverlay() => new UserModSelectScreen();
protected virtual void ApplyFilterToCarousel(FilterCriteria criteria) protected virtual void ApplyFilterToCarousel(FilterCriteria criteria)
{ {
@ -658,6 +659,7 @@ namespace osu.Game.Screens.Select
return true; return true;
beatmapInfoWedge.Hide(); beatmapInfoWedge.Hide();
ModSelect.Hide();
this.FadeOut(100); this.FadeOut(100);
@ -716,6 +718,8 @@ namespace osu.Game.Screens.Select
if (music != null) if (music != null)
music.TrackChanged -= ensureTrackLooping; music.TrackChanged -= ensureTrackLooping;
modSelectOverlayRegistration?.Dispose();
} }
/// <summary> /// <summary>

View File

@ -1,12 +1,15 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Development; using osu.Framework.Development;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Graphics;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Screens; using osu.Game.Screens;
@ -15,11 +18,12 @@ namespace osu.Game.Tests.Visual
/// <summary> /// <summary>
/// A test case which can be used to test a screen (that relies on OnEntering being called to execute startup instructions). /// A test case which can be used to test a screen (that relies on OnEntering being called to execute startup instructions).
/// </summary> /// </summary>
public abstract class ScreenTestScene : OsuManualInputManagerTestScene public abstract class ScreenTestScene : OsuManualInputManagerTestScene, IOverlayManager
{ {
protected readonly OsuScreenStack Stack; protected readonly OsuScreenStack Stack;
private readonly Container content; private readonly Container content;
private readonly Container overlayContent;
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
@ -36,7 +40,11 @@ namespace osu.Game.Tests.Visual
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}, },
content = new Container { RelativeSizeAxes = Axes.Both }, content = new Container { RelativeSizeAxes = Axes.Both },
DialogOverlay = new DialogOverlay() overlayContent = new Container
{
RelativeSizeAxes = Axes.Both,
Child = DialogOverlay = new DialogOverlay()
}
}); });
Stack.ScreenPushed += (lastScreen, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed → {newScreen}"); Stack.ScreenPushed += (lastScreen, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed → {newScreen}");
@ -65,5 +73,26 @@ namespace osu.Game.Tests.Visual
return false; return false;
}); });
} }
#region IOverlayManager
IBindable<OverlayActivation> IOverlayManager.OverlayActivationMode { get; } = new Bindable<OverlayActivation>(OverlayActivation.All);
// in the blocking methods below it is important to be careful about threading (e.g. use `Expire()` rather than `Remove()`, and schedule transforms),
// because in the worst case the clean-up methods could be called from async disposal.
IDisposable IOverlayManager.RegisterBlockingOverlay(OverlayContainer overlayContainer)
{
overlayContent.Add(overlayContainer);
return new InvokeOnDisposal(() => overlayContainer.Expire());
}
void IOverlayManager.ShowBlockingOverlay(OverlayContainer overlay)
=> Schedule(() => Stack.FadeColour(OsuColour.Gray(0.5f), 500, Easing.OutQuint));
void IOverlayManager.HideBlockingOverlay(OverlayContainer overlay)
=> Schedule(() => Stack.FadeColour(Colour4.White, 500, Easing.OutQuint));
#endregion
} }
} }