mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 02:32:55 +08:00
Merge pull request #28683 from frenzibyte/footer-v2-integration
Replace local footer in existing sheared overlays (e.g. mod select & first-run setup) with `ScreenFooter`
This commit is contained in:
commit
eb3f480a2a
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -16,6 +17,7 @@ using osu.Framework.Testing;
|
|||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
using osu.Game.Screens.OnlinePlay;
|
using osu.Game.Screens.OnlinePlay;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -26,6 +28,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
private FreeModSelectOverlay freeModSelectOverlay;
|
private FreeModSelectOverlay freeModSelectOverlay;
|
||||||
private FooterButtonFreeMods footerButtonFreeMods;
|
private FooterButtonFreeMods footerButtonFreeMods;
|
||||||
|
private ScreenFooter footer;
|
||||||
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> availableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> availableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -127,7 +130,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
createFreeModSelect();
|
createFreeModSelect();
|
||||||
|
|
||||||
AddAssert("overlay select all button enabled", () => freeModSelectOverlay.ChildrenOfType<SelectAllModsButton>().Single().Enabled.Value);
|
AddAssert("overlay select all button enabled", () => this.ChildrenOfType<SelectAllModsButton>().Single().Enabled.Value);
|
||||||
AddAssert("footer button displays off", () => footerButtonFreeMods.ChildrenOfType<IHasText>().Any(t => t.Text == "off"));
|
AddAssert("footer button displays off", () => footerButtonFreeMods.ChildrenOfType<IHasText>().Any(t => t.Text == "off"));
|
||||||
|
|
||||||
AddStep("click footer select all button", () =>
|
AddStep("click footer select all button", () =>
|
||||||
@ -150,7 +153,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
private void createFreeModSelect()
|
private void createFreeModSelect()
|
||||||
{
|
{
|
||||||
AddStep("create free mod select screen", () => Children = new Drawable[]
|
AddStep("create free mod select screen", () => Child = new DependencyProvidingContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
freeModSelectOverlay = new FreeModSelectOverlay
|
freeModSelectOverlay = new FreeModSelectOverlay
|
||||||
{
|
{
|
||||||
@ -160,9 +166,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
|
Y = -ScreenFooter.HEIGHT,
|
||||||
Current = { BindTarget = freeModSelectOverlay.SelectedMods },
|
Current = { BindTarget = freeModSelectOverlay.SelectedMods },
|
||||||
},
|
},
|
||||||
|
footer = new ScreenFooter(),
|
||||||
|
},
|
||||||
|
CachedDependencies = new (Type, object)[] { (typeof(ScreenFooter), footer) },
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("all column content loaded",
|
AddUntilStep("all column content loaded",
|
||||||
() => freeModSelectOverlay.ChildrenOfType<ModColumn>().Any()
|
() => freeModSelectOverlay.ChildrenOfType<ModColumn>().Any()
|
||||||
&& freeModSelectOverlay.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded));
|
&& freeModSelectOverlay.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded));
|
||||||
|
@ -312,14 +312,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddUntilStep("wait for join", () => RoomJoined);
|
AddUntilStep("wait for join", () => RoomJoined);
|
||||||
|
|
||||||
ClickButtonWhenEnabled<UserModSelectButton>();
|
ClickButtonWhenEnabled<UserModSelectButton>();
|
||||||
AddAssert("mod select shows unranked", () => screen.UserModsSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().Ranked.Value == false);
|
AddAssert("mod select shows unranked", () => this.ChildrenOfType<RankingInformationDisplay>().Single().Ranked.Value == false);
|
||||||
AddAssert("score multiplier = 1.20", () => screen.UserModsSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01));
|
AddAssert("score multiplier = 1.20", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01));
|
||||||
|
|
||||||
AddStep("select flashlight", () => screen.UserModsSelectOverlay.ChildrenOfType<ModPanel>().Single(m => m.Mod is ModFlashlight).TriggerClick());
|
AddStep("select flashlight", () => screen.UserModsSelectOverlay.ChildrenOfType<ModPanel>().Single(m => m.Mod is ModFlashlight).TriggerClick());
|
||||||
AddAssert("score multiplier = 1.35", () => screen.UserModsSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.35).Within(0.01));
|
AddAssert("score multiplier = 1.35", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.35).Within(0.01));
|
||||||
|
|
||||||
AddStep("change flashlight setting", () => ((OsuModFlashlight)screen.UserModsSelectOverlay.SelectedMods.Value.Single()).FollowDelay.Value = 1200);
|
AddStep("change flashlight setting", () => ((OsuModFlashlight)screen.UserModsSelectOverlay.SelectedMods.Value.Single()).FollowDelay.Value = 1200);
|
||||||
AddAssert("score multiplier = 1.20", () => screen.UserModsSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01));
|
AddAssert("score multiplier = 1.20", () => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01));
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class TestMultiplayerMatchSubScreen : MultiplayerMatchSubScreen
|
private partial class TestMultiplayerMatchSubScreen : MultiplayerMatchSubScreen
|
||||||
|
@ -181,12 +181,6 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
Stack.Padding = new MarginPadding { Bottom = screenScreenFooter.DrawHeight - screenScreenFooter.Y };
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateFooter(IScreen? _, IScreen? newScreen)
|
private void updateFooter(IScreen? _, IScreen? newScreen)
|
||||||
{
|
{
|
||||||
if (newScreen is IOsuScreen osuScreen && osuScreen.ShowFooter)
|
if (newScreen is IOsuScreen osuScreen && osuScreen.ShowFooter)
|
||||||
|
@ -11,6 +11,8 @@ using Moq;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
@ -20,6 +22,7 @@ using osu.Game.Overlays;
|
|||||||
using osu.Game.Overlays.FirstRunSetup;
|
using osu.Game.Overlays.FirstRunSetup;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
@ -28,6 +31,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
public partial class TestSceneFirstRunSetupOverlay : OsuManualInputManagerTestScene
|
public partial class TestSceneFirstRunSetupOverlay : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
private FirstRunSetupOverlay overlay;
|
private FirstRunSetupOverlay overlay;
|
||||||
|
private ScreenFooter footer;
|
||||||
|
|
||||||
private readonly Mock<TestPerformerFromScreenRunner> performer = new Mock<TestPerformerFromScreenRunner>();
|
private readonly Mock<TestPerformerFromScreenRunner> performer = new Mock<TestPerformerFromScreenRunner>();
|
||||||
|
|
||||||
@ -60,19 +64,16 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
.Callback((Notification n) => lastNotification = n);
|
.Callback((Notification n) => lastNotification = n);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("add overlay", () =>
|
createOverlay();
|
||||||
{
|
|
||||||
Child = overlay = new FirstRunSetupOverlay
|
AddStep("show overlay", () => overlay.Show());
|
||||||
{
|
|
||||||
State = { Value = Visibility.Visible }
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBasic()
|
public void TestBasic()
|
||||||
{
|
{
|
||||||
AddAssert("overlay visible", () => overlay.State.Value == Visibility.Visible);
|
AddAssert("overlay visible", () => overlay.State.Value == Visibility.Visible);
|
||||||
|
AddAssert("footer visible", () => footer.State.Value == Visibility.Visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -82,16 +83,13 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
AddUntilStep("step through", () =>
|
AddUntilStep("step through", () =>
|
||||||
{
|
{
|
||||||
if (overlay.CurrentScreen?.IsLoaded != false) overlay.NextButton.TriggerClick();
|
if (overlay.CurrentScreen?.IsLoaded != false) overlay.NextButton.AsNonNull().TriggerClick();
|
||||||
return overlay.State.Value == Visibility.Hidden;
|
return overlay.State.Value == Visibility.Hidden;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("first run false", () => !LocalConfig.Get<bool>(OsuSetting.ShowFirstRunSetup));
|
AddAssert("first run false", () => !LocalConfig.Get<bool>(OsuSetting.ShowFirstRunSetup));
|
||||||
|
|
||||||
AddStep("add overlay", () =>
|
createOverlay();
|
||||||
{
|
|
||||||
Child = overlay = new FirstRunSetupOverlay();
|
|
||||||
});
|
|
||||||
|
|
||||||
AddWaitStep("wait some", 5);
|
AddWaitStep("wait some", 5);
|
||||||
|
|
||||||
@ -109,7 +107,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
if (keyboard)
|
if (keyboard)
|
||||||
InputManager.Key(Key.Enter);
|
InputManager.Key(Key.Enter);
|
||||||
else
|
else
|
||||||
overlay.NextButton.TriggerClick();
|
overlay.NextButton.AsNonNull().TriggerClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
return overlay.State.Value == Visibility.Hidden;
|
return overlay.State.Value == Visibility.Hidden;
|
||||||
@ -128,11 +126,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
public void TestBackButton(bool keyboard)
|
public void TestBackButton(bool keyboard)
|
||||||
{
|
{
|
||||||
AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value);
|
|
||||||
|
|
||||||
AddUntilStep("step to last", () =>
|
AddUntilStep("step to last", () =>
|
||||||
{
|
{
|
||||||
var nextButton = overlay.NextButton;
|
var nextButton = overlay.NextButton.AsNonNull();
|
||||||
|
|
||||||
if (overlay.CurrentScreen?.IsLoaded != false)
|
if (overlay.CurrentScreen?.IsLoaded != false)
|
||||||
nextButton.TriggerClick();
|
nextButton.TriggerClick();
|
||||||
@ -142,24 +138,29 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
AddUntilStep("step back to start", () =>
|
AddUntilStep("step back to start", () =>
|
||||||
{
|
{
|
||||||
if (overlay.CurrentScreen?.IsLoaded != false)
|
if (overlay.CurrentScreen?.IsLoaded != false && !(overlay.CurrentScreen is ScreenWelcome))
|
||||||
{
|
{
|
||||||
if (keyboard)
|
if (keyboard)
|
||||||
InputManager.Key(Key.Escape);
|
InputManager.Key(Key.Escape);
|
||||||
else
|
else
|
||||||
overlay.BackButton.TriggerClick();
|
footer.BackButton.TriggerClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
return overlay.CurrentScreen is ScreenWelcome;
|
return overlay.CurrentScreen is ScreenWelcome;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("back button disabled", () => !overlay.BackButton.Enabled.Value);
|
AddAssert("overlay not dismissed", () => overlay.State.Value == Visibility.Visible);
|
||||||
|
|
||||||
if (keyboard)
|
if (keyboard)
|
||||||
{
|
{
|
||||||
AddStep("exit via keyboard", () => InputManager.Key(Key.Escape));
|
AddStep("exit via keyboard", () => InputManager.Key(Key.Escape));
|
||||||
AddAssert("overlay dismissed", () => overlay.State.Value == Visibility.Hidden);
|
AddAssert("overlay dismissed", () => overlay.State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddStep("press back button", () => footer.BackButton.TriggerClick());
|
||||||
|
AddAssert("overlay dismissed", () => overlay.State.Value == Visibility.Hidden);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -185,7 +186,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestResumeViaNotification()
|
public void TestResumeViaNotification()
|
||||||
{
|
{
|
||||||
AddStep("step to next", () => overlay.NextButton.TriggerClick());
|
AddStep("step to next", () => overlay.NextButton.AsNonNull().TriggerClick());
|
||||||
|
|
||||||
AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenUIScale);
|
AddAssert("is at known screen", () => overlay.CurrentScreen is ScreenUIScale);
|
||||||
|
|
||||||
@ -200,6 +201,27 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddAssert("is resumed", () => overlay.CurrentScreen is ScreenUIScale);
|
AddAssert("is resumed", () => overlay.CurrentScreen is ScreenUIScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createOverlay()
|
||||||
|
{
|
||||||
|
AddStep("add overlay", () =>
|
||||||
|
{
|
||||||
|
var receptor = new ScreenFooter.BackReceptor();
|
||||||
|
footer = new ScreenFooter(receptor);
|
||||||
|
|
||||||
|
Child = new DependencyProvidingContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
CachedDependencies = new[] { (typeof(ScreenFooter), (object)footer) },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
receptor,
|
||||||
|
overlay = new FirstRunSetupOverlay(),
|
||||||
|
footer,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// interface mocks break hot reload, mocking this stub implementation instead works around it.
|
// interface mocks break hot reload, mocking this stub implementation instead works around it.
|
||||||
// see: https://github.com/moq/moq4/issues/1252
|
// see: https://github.com/moq/moq4/issues/1252
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
|
@ -24,6 +24,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Mods;
|
using osu.Game.Rulesets.Taiko.Mods;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
using osu.Game.Tests.Mods;
|
using osu.Game.Tests.Mods;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -94,12 +95,28 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
private void createScreen()
|
private void createScreen()
|
||||||
{
|
{
|
||||||
AddStep("create screen", () => Child = modSelectOverlay = new TestModSelectOverlay
|
AddStep("create screen", () =>
|
||||||
|
{
|
||||||
|
var receptor = new ScreenFooter.BackReceptor();
|
||||||
|
var footer = new ScreenFooter(receptor);
|
||||||
|
|
||||||
|
Child = new DependencyProvidingContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
CachedDependencies = new[] { (typeof(ScreenFooter), (object)footer) },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
receptor,
|
||||||
|
modSelectOverlay = new TestModSelectOverlay
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
State = { Value = Visibility.Visible },
|
State = { Value = Visibility.Visible },
|
||||||
Beatmap = Beatmap.Value,
|
Beatmap = { Value = Beatmap.Value },
|
||||||
SelectedMods = { BindTarget = SelectedMods }
|
SelectedMods = { BindTarget = SelectedMods },
|
||||||
|
},
|
||||||
|
footer,
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
waitForColumnLoad();
|
waitForColumnLoad();
|
||||||
}
|
}
|
||||||
@ -120,7 +137,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddAssert("mod multiplier correct", () =>
|
AddAssert("mod multiplier correct", () =>
|
||||||
{
|
{
|
||||||
double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier);
|
double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier);
|
||||||
return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value);
|
return Precision.AlmostEquals(multiplier, this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value);
|
||||||
});
|
});
|
||||||
assertCustomisationToggleState(disabled: false, active: false);
|
assertCustomisationToggleState(disabled: false, active: false);
|
||||||
AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType<ISettingsItem>().Any());
|
AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType<ISettingsItem>().Any());
|
||||||
@ -135,7 +152,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddAssert("mod multiplier correct", () =>
|
AddAssert("mod multiplier correct", () =>
|
||||||
{
|
{
|
||||||
double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier);
|
double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier);
|
||||||
return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value);
|
return Precision.AlmostEquals(multiplier, this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value);
|
||||||
});
|
});
|
||||||
assertCustomisationToggleState(disabled: false, active: false);
|
assertCustomisationToggleState(disabled: false, active: false);
|
||||||
AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType<ISettingsItem>().Any());
|
AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType<ISettingsItem>().Any());
|
||||||
@ -757,7 +774,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
AddStep("click back button", () =>
|
AddStep("click back button", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(modSelectOverlay.BackButton);
|
InputManager.MoveMouseTo(this.ChildrenOfType<ScreenBackButton>().Single());
|
||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
});
|
});
|
||||||
AddAssert("mod select hidden", () => modSelectOverlay.State.Value == Visibility.Hidden);
|
AddAssert("mod select hidden", () => modSelectOverlay.State.Value == Visibility.Hidden);
|
||||||
@ -885,7 +902,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
});
|
});
|
||||||
AddAssert("difficulty multiplier display shows correct value",
|
AddAssert("difficulty multiplier display shows correct value",
|
||||||
() => modSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(0.1).Within(Precision.DOUBLE_EPSILON));
|
() => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(0.1).Within(Precision.DOUBLE_EPSILON));
|
||||||
|
|
||||||
// this is highly unorthodox in a test, but because the `ModSettingChangeTracker` machinery heavily leans on events and object disposal and re-creation,
|
// this is highly unorthodox in a test, but because the `ModSettingChangeTracker` machinery heavily leans on events and object disposal and re-creation,
|
||||||
// it is instrumental in the reproduction of the failure scenario that this test is supposed to cover.
|
// it is instrumental in the reproduction of the failure scenario that this test is supposed to cover.
|
||||||
@ -895,7 +912,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddStep("reset half time speed to default", () => modSelectOverlay.ChildrenOfType<ModCustomisationPanel>().Single()
|
AddStep("reset half time speed to default", () => modSelectOverlay.ChildrenOfType<ModCustomisationPanel>().Single()
|
||||||
.ChildrenOfType<RevertToDefaultButton<double>>().Single().TriggerClick());
|
.ChildrenOfType<RevertToDefaultButton<double>>().Single().TriggerClick());
|
||||||
AddUntilStep("difficulty multiplier display shows correct value",
|
AddUntilStep("difficulty multiplier display shows correct value",
|
||||||
() => modSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(0.3).Within(Precision.DOUBLE_EPSILON));
|
() => this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(0.3).Within(Precision.DOUBLE_EPSILON));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -1015,8 +1032,6 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
private partial class TestModSelectOverlay : UserModSelectOverlay
|
private partial class TestModSelectOverlay : UserModSelectOverlay
|
||||||
{
|
{
|
||||||
protected override bool ShowPresets => true;
|
protected override bool ShowPresets => true;
|
||||||
|
|
||||||
public new ShearedButton BackButton => base.BackButton;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestUnimplementedMod : Mod
|
private class TestUnimplementedMod : Mod
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
private DependencyProvidingContainer contentContainer = null!;
|
private DependencyProvidingContainer contentContainer = null!;
|
||||||
private ScreenFooter screenFooter = null!;
|
private ScreenFooter screenFooter = null!;
|
||||||
private TestModSelectOverlay overlay = null!;
|
private TestModSelectOverlay modOverlay = null!;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
},
|
},
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
overlay = new TestModSelectOverlay(),
|
modOverlay = new TestModSelectOverlay(),
|
||||||
new PopoverContainer
|
new PopoverContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
screenFooter.SetButtons(new ScreenFooterButton[]
|
screenFooter.SetButtons(new ScreenFooterButton[]
|
||||||
{
|
{
|
||||||
new ScreenFooterButtonMods(overlay) { Current = SelectedMods },
|
new ScreenFooterButtonMods(modOverlay) { Current = SelectedMods },
|
||||||
new ScreenFooterButtonRandom(),
|
new ScreenFooterButtonRandom(),
|
||||||
new ScreenFooterButtonOptions(),
|
new ScreenFooterButtonOptions(),
|
||||||
});
|
});
|
||||||
@ -178,6 +178,24 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddAssert("footer hidden", () => screenFooter.State.Value == Visibility.Hidden);
|
AddAssert("footer hidden", () => screenFooter.State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLoadOverlayAfterFooterIsDisplayed()
|
||||||
|
{
|
||||||
|
TestShearedOverlayContainer externalOverlay = null!;
|
||||||
|
|
||||||
|
AddStep("show mod overlay", () => modOverlay.Show());
|
||||||
|
AddUntilStep("mod footer content shown", () => this.ChildrenOfType<ModSelectFooterContent>().SingleOrDefault()?.IsPresent, () => Is.True);
|
||||||
|
|
||||||
|
AddStep("add external overlay", () => contentContainer.Add(externalOverlay = new TestShearedOverlayContainer()));
|
||||||
|
AddUntilStep("wait for load", () => externalOverlay.IsLoaded);
|
||||||
|
AddAssert("mod footer content still shown", () => this.ChildrenOfType<ModSelectFooterContent>().SingleOrDefault()?.IsPresent, () => Is.True);
|
||||||
|
AddAssert("external overlay content not shown", () => this.ChildrenOfType<TestShearedOverlayContainer.TestFooterContent>().SingleOrDefault()?.IsPresent, () => Is.Not.True);
|
||||||
|
|
||||||
|
AddStep("hide mod overlay", () => modOverlay.Hide());
|
||||||
|
AddUntilStep("mod footer content hidden", () => this.ChildrenOfType<ModSelectFooterContent>().SingleOrDefault()?.IsPresent, () => Is.Not.True);
|
||||||
|
AddAssert("external overlay content still not shown", () => this.ChildrenOfType<TestShearedOverlayContainer.TestFooterContent>().SingleOrDefault()?.IsPresent, () => Is.Not.True);
|
||||||
|
}
|
||||||
|
|
||||||
private partial class TestModSelectOverlay : UserModSelectOverlay
|
private partial class TestModSelectOverlay : UserModSelectOverlay
|
||||||
{
|
{
|
||||||
protected override bool ShowPresets => true;
|
protected override bool ShowPresets => true;
|
||||||
@ -185,8 +203,6 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
private partial class TestShearedOverlayContainer : ShearedOverlayContainer
|
private partial class TestShearedOverlayContainer : ShearedOverlayContainer
|
||||||
{
|
{
|
||||||
public override bool UseNewFooter => true;
|
|
||||||
|
|
||||||
public TestShearedOverlayContainer()
|
public TestShearedOverlayContainer()
|
||||||
: base(OverlayColourScheme.Orange)
|
: base(OverlayColourScheme.Orange)
|
||||||
{
|
{
|
||||||
@ -212,7 +228,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Drawable CreateFooterContent() => new TestFooterContent();
|
public override VisibilityContainer CreateFooterContent() => new TestFooterContent();
|
||||||
|
|
||||||
public partial class TestFooterContent : VisibilityContainer
|
public partial class TestFooterContent : VisibilityContainer
|
||||||
{
|
{
|
||||||
|
@ -51,6 +51,7 @@ using osu.Game.Online.Chat;
|
|||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.BeatmapListing;
|
using osu.Game.Overlays.BeatmapListing;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Overlays.Music;
|
using osu.Game.Overlays.Music;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Overlays.SkinEditor;
|
using osu.Game.Overlays.SkinEditor;
|
||||||
@ -83,7 +84,7 @@ namespace osu.Game
|
|||||||
public partial class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>, ILocalUserPlayInfo, IPerformFromScreenRunner, IOverlayManager, ILinkHandler
|
public partial class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>, ILocalUserPlayInfo, IPerformFromScreenRunner, IOverlayManager, ILinkHandler
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// Different port allows runnning release and debug builds alongside each other.
|
// Different port allows running release and debug builds alongside each other.
|
||||||
public const int IPC_PORT = 44824;
|
public const int IPC_PORT = 44824;
|
||||||
#else
|
#else
|
||||||
public const int IPC_PORT = 44823;
|
public const int IPC_PORT = 44823;
|
||||||
@ -132,6 +133,8 @@ namespace osu.Game
|
|||||||
|
|
||||||
private Container topMostOverlayContent;
|
private Container topMostOverlayContent;
|
||||||
|
|
||||||
|
private Container footerBasedOverlayContent;
|
||||||
|
|
||||||
protected ScalingContainer ScreenContainer { get; private set; }
|
protected ScalingContainer ScreenContainer { get; private set; }
|
||||||
|
|
||||||
protected Container ScreenOffsetContainer { get; private set; }
|
protected Container ScreenOffsetContainer { get; private set; }
|
||||||
@ -156,8 +159,6 @@ namespace osu.Game
|
|||||||
|
|
||||||
private float toolbarOffset => (Toolbar?.Position.Y ?? 0) + (Toolbar?.DrawHeight ?? 0);
|
private float toolbarOffset => (Toolbar?.Position.Y ?? 0) + (Toolbar?.DrawHeight ?? 0);
|
||||||
|
|
||||||
private float screenFooterOffset => (ScreenFooter?.DrawHeight ?? 0) - (ScreenFooter?.Position.Y ?? 0);
|
|
||||||
|
|
||||||
private IdleTracker idleTracker;
|
private IdleTracker idleTracker;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -242,6 +243,10 @@ namespace osu.Game
|
|||||||
throw new ArgumentException($@"{overlayContainer} has already been registered via {nameof(IOverlayManager.RegisterBlockingOverlay)} once.");
|
throw new ArgumentException($@"{overlayContainer} has already been registered via {nameof(IOverlayManager.RegisterBlockingOverlay)} once.");
|
||||||
|
|
||||||
externalOverlays.Add(overlayContainer);
|
externalOverlays.Add(overlayContainer);
|
||||||
|
|
||||||
|
if (overlayContainer is ShearedOverlayContainer)
|
||||||
|
footerBasedOverlayContent.Add(overlayContainer);
|
||||||
|
else
|
||||||
overlayContent.Add(overlayContainer);
|
overlayContent.Add(overlayContainer);
|
||||||
|
|
||||||
if (overlayContainer is OsuFocusedOverlayContainer focusedOverlayContainer)
|
if (overlayContainer is OsuFocusedOverlayContainer focusedOverlayContainer)
|
||||||
@ -934,7 +939,6 @@ namespace osu.Game
|
|||||||
return string.Join(" / ", combinations);
|
return string.Join(" / ", combinations);
|
||||||
};
|
};
|
||||||
|
|
||||||
Container logoContainer;
|
|
||||||
ScreenFooter.BackReceptor backReceptor;
|
ScreenFooter.BackReceptor backReceptor;
|
||||||
|
|
||||||
dependencies.CacheAs(idleTracker = new GameIdleTracker(6000));
|
dependencies.CacheAs(idleTracker = new GameIdleTracker(6000));
|
||||||
@ -948,6 +952,8 @@ namespace osu.Game
|
|||||||
|
|
||||||
Add(sessionIdleTracker);
|
Add(sessionIdleTracker);
|
||||||
|
|
||||||
|
Container logoContainer;
|
||||||
|
|
||||||
AddRange(new Drawable[]
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
new VolumeControlReceptor
|
new VolumeControlReceptor
|
||||||
@ -976,11 +982,19 @@ namespace osu.Game
|
|||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
Action = () => ScreenFooter.OnBack?.Invoke(),
|
Action = () => ScreenFooter.OnBack?.Invoke(),
|
||||||
},
|
},
|
||||||
|
logoContainer = new Container { RelativeSizeAxes = Axes.Both },
|
||||||
|
footerBasedOverlayContent = new Container
|
||||||
|
{
|
||||||
|
Depth = -1,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
new PopoverContainer
|
new PopoverContainer
|
||||||
{
|
{
|
||||||
|
Depth = -1,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = ScreenFooter = new ScreenFooter(backReceptor)
|
Child = ScreenFooter = new ScreenFooter(backReceptor)
|
||||||
{
|
{
|
||||||
|
RequestLogoInFront = inFront => ScreenContainer.ChangeChildDepth(logoContainer, inFront ? float.MinValue : 0),
|
||||||
OnBack = () =>
|
OnBack = () =>
|
||||||
{
|
{
|
||||||
if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen))
|
if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen))
|
||||||
@ -991,7 +1005,6 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
logoContainer = new Container { RelativeSizeAxes = Axes.Both },
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1025,7 +1038,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
if (!IsDeployedBuild)
|
if (!IsDeployedBuild)
|
||||||
{
|
{
|
||||||
dependencies.Cache(versionManager = new VersionManager { Depth = int.MinValue });
|
dependencies.Cache(versionManager = new VersionManager());
|
||||||
loadComponentSingleFile(versionManager, ScreenContainer.Add);
|
loadComponentSingleFile(versionManager, ScreenContainer.Add);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1072,7 +1085,7 @@ namespace osu.Game
|
|||||||
loadComponentSingleFile(CreateUpdateManager(), Add, true);
|
loadComponentSingleFile(CreateUpdateManager(), Add, true);
|
||||||
|
|
||||||
// overlay elements
|
// overlay elements
|
||||||
loadComponentSingleFile(FirstRunOverlay = new FirstRunSetupOverlay(), overlayContent.Add, true);
|
loadComponentSingleFile(FirstRunOverlay = new FirstRunSetupOverlay(), footerBasedOverlayContent.Add, true);
|
||||||
loadComponentSingleFile(new ManageCollectionsDialog(), overlayContent.Add, true);
|
loadComponentSingleFile(new ManageCollectionsDialog(), overlayContent.Add, true);
|
||||||
loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true);
|
loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true);
|
||||||
loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true);
|
loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true);
|
||||||
@ -1137,7 +1150,7 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ensure only one of these overlays are open at once.
|
// ensure only one of these overlays are open at once.
|
||||||
var singleDisplayOverlays = new OverlayContainer[] { FirstRunOverlay, chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay, wikiOverlay };
|
var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay, wikiOverlay };
|
||||||
|
|
||||||
foreach (var overlay in singleDisplayOverlays)
|
foreach (var overlay in singleDisplayOverlays)
|
||||||
{
|
{
|
||||||
@ -1485,7 +1498,6 @@ namespace osu.Game
|
|||||||
|
|
||||||
ScreenOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset };
|
ScreenOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset };
|
||||||
overlayOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset };
|
overlayOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset };
|
||||||
ScreenStack.Padding = new MarginPadding { Bottom = screenFooterOffset };
|
|
||||||
|
|
||||||
float horizontalOffset = 0f;
|
float horizontalOffset = 0f;
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ using osu.Game.Overlays.Settings;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
@ -153,6 +154,7 @@ namespace osu.Game.Overlays.FirstRunSetup
|
|||||||
|
|
||||||
OsuScreenStack stack;
|
OsuScreenStack stack;
|
||||||
OsuLogo logo;
|
OsuLogo logo;
|
||||||
|
ScreenFooter footer;
|
||||||
|
|
||||||
Padding = new MarginPadding(5);
|
Padding = new MarginPadding(5);
|
||||||
|
|
||||||
@ -166,7 +168,8 @@ namespace osu.Game.Overlays.FirstRunSetup
|
|||||||
{
|
{
|
||||||
RelativePositionAxes = Axes.Both,
|
RelativePositionAxes = Axes.Both,
|
||||||
Position = new Vector2(0.5f),
|
Position = new Vector2(0.5f),
|
||||||
})
|
}),
|
||||||
|
(typeof(ScreenFooter), footer = new ScreenFooter()),
|
||||||
},
|
},
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -178,7 +181,8 @@ namespace osu.Game.Overlays.FirstRunSetup
|
|||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
stack = new OsuScreenStack(),
|
stack = new OsuScreenStack(),
|
||||||
logo
|
footer,
|
||||||
|
logo,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ using osu.Game.Overlays.FirstRunSetup;
|
|||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
@ -44,8 +45,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private ScreenStack? stack;
|
private ScreenStack? stack;
|
||||||
|
|
||||||
public ShearedButton NextButton = null!;
|
public ShearedButton? NextButton => DisplayedFooterContent?.NextButton;
|
||||||
public ShearedButton BackButton = null!;
|
|
||||||
|
|
||||||
private readonly Bindable<bool> showFirstRunSetup = new Bindable<bool>();
|
private readonly Bindable<bool> showFirstRunSetup = new Bindable<bool>();
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ namespace osu.Game.Overlays
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Bottom = 20, },
|
Padding = new MarginPadding { Bottom = 20 },
|
||||||
Child = new GridContainer
|
Child = new GridContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -134,51 +134,6 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
FooterContent.Add(new GridContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Margin = new MarginPadding { Vertical = PADDING },
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
ColumnDimensions = new[]
|
|
||||||
{
|
|
||||||
new Dimension(GridSizeMode.Absolute, 10),
|
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
|
||||||
new Dimension(),
|
|
||||||
new Dimension(GridSizeMode.Absolute, 10),
|
|
||||||
},
|
|
||||||
RowDimensions = new[]
|
|
||||||
{
|
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
|
||||||
},
|
|
||||||
Content = new[]
|
|
||||||
{
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
Empty(),
|
|
||||||
BackButton = new ShearedButton(300)
|
|
||||||
{
|
|
||||||
Text = CommonStrings.Back,
|
|
||||||
Action = showPreviousStep,
|
|
||||||
Enabled = { Value = false },
|
|
||||||
DarkerColour = colours.Pink2,
|
|
||||||
LighterColour = colours.Pink1,
|
|
||||||
},
|
|
||||||
NextButton = new ShearedButton(0)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Width = 1,
|
|
||||||
Text = FirstRunSetupOverlayStrings.GetStarted,
|
|
||||||
DarkerColour = ColourProvider.Colour2,
|
|
||||||
LighterColour = ColourProvider.Colour1,
|
|
||||||
Action = showNextStep
|
|
||||||
},
|
|
||||||
Empty(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -190,6 +145,36 @@ namespace osu.Game.Overlays
|
|||||||
if (showFirstRunSetup.Value) Show();
|
if (showFirstRunSetup.Value) Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private ScreenFooter footer { get; set; } = null!;
|
||||||
|
|
||||||
|
public new FirstRunSetupFooterContent? DisplayedFooterContent => base.DisplayedFooterContent as FirstRunSetupFooterContent;
|
||||||
|
|
||||||
|
public override VisibilityContainer CreateFooterContent()
|
||||||
|
{
|
||||||
|
var footerContent = new FirstRunSetupFooterContent
|
||||||
|
{
|
||||||
|
ShowNextStep = showNextStep,
|
||||||
|
};
|
||||||
|
|
||||||
|
footerContent.OnLoadComplete += _ => updateButtons();
|
||||||
|
return footerContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnBackButton()
|
||||||
|
{
|
||||||
|
if (currentStepIndex == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Debug.Assert(stack != null);
|
||||||
|
|
||||||
|
stack.CurrentScreen.Exit();
|
||||||
|
currentStepIndex--;
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
{
|
{
|
||||||
if (!e.Repeat)
|
if (!e.Repeat)
|
||||||
@ -197,19 +182,12 @@ namespace osu.Game.Overlays
|
|||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
case GlobalAction.Select:
|
case GlobalAction.Select:
|
||||||
NextButton.TriggerClick();
|
DisplayedFooterContent?.NextButton.TriggerClick();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.Back:
|
case GlobalAction.Back:
|
||||||
if (BackButton.Enabled.Value)
|
footer.BackButton.TriggerClick();
|
||||||
{
|
return false;
|
||||||
BackButton.TriggerClick();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If back button is disabled, we are at the first step.
|
|
||||||
// The base call will handle dismissal of the overlay.
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,19 +257,6 @@ namespace osu.Game.Overlays
|
|||||||
showNextStep();
|
showNextStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showPreviousStep()
|
|
||||||
{
|
|
||||||
if (currentStepIndex == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Debug.Assert(stack != null);
|
|
||||||
|
|
||||||
stack.CurrentScreen.Exit();
|
|
||||||
currentStepIndex--;
|
|
||||||
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showNextStep()
|
private void showNextStep()
|
||||||
{
|
{
|
||||||
Debug.Assert(currentStepIndex != null);
|
Debug.Assert(currentStepIndex != null);
|
||||||
@ -322,29 +287,61 @@ namespace osu.Game.Overlays
|
|||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateButtons()
|
private void updateButtons() => DisplayedFooterContent?.UpdateButtons(currentStepIndex, steps);
|
||||||
{
|
|
||||||
BackButton.Enabled.Value = currentStepIndex > 0;
|
|
||||||
NextButton.Enabled.Value = currentStepIndex != null;
|
|
||||||
|
|
||||||
if (currentStepIndex == null)
|
public partial class FirstRunSetupFooterContent : VisibilityContainer
|
||||||
|
{
|
||||||
|
public ShearedButton NextButton { get; private set; } = null!;
|
||||||
|
|
||||||
|
public Action? ShowNextStep;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = NextButton = new ShearedButton(0)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Margin = new MarginPadding { Right = 12f },
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Width = 1,
|
||||||
|
Text = FirstRunSetupOverlayStrings.GetStarted,
|
||||||
|
DarkerColour = colourProvider.Colour2,
|
||||||
|
LighterColour = colourProvider.Colour1,
|
||||||
|
Action = () => ShowNextStep?.Invoke(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateButtons(int? currentStep, IReadOnlyList<Type> steps)
|
||||||
|
{
|
||||||
|
NextButton.Enabled.Value = currentStep != null;
|
||||||
|
|
||||||
|
if (currentStep == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool isFirstStep = currentStepIndex == 0;
|
bool isFirstStep = currentStep == 0;
|
||||||
bool isLastStep = currentStepIndex == steps.Count - 1;
|
bool isLastStep = currentStep == steps.Count - 1;
|
||||||
|
|
||||||
if (isFirstStep)
|
if (isFirstStep)
|
||||||
{
|
|
||||||
BackButton.Text = CommonStrings.Back;
|
|
||||||
NextButton.Text = FirstRunSetupOverlayStrings.GetStarted;
|
NextButton.Text = FirstRunSetupOverlayStrings.GetStarted;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BackButton.Text = LocalisableString.Interpolate($@"{CommonStrings.Back} ({steps[currentStepIndex.Value - 1].GetLocalisableDescription()})");
|
|
||||||
|
|
||||||
NextButton.Text = isLastStep
|
NextButton.Text = isLastStep
|
||||||
? CommonStrings.Finish
|
? CommonStrings.Finish
|
||||||
: LocalisableString.Interpolate($@"{CommonStrings.Next} ({steps[currentStepIndex.Value + 1].GetLocalisableDescription()})");
|
: LocalisableString.Interpolate($@"{CommonStrings.Next} ({steps[currentStep.Value + 1].GetLocalisableDescription()})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
this.FadeIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
this.Delay(400).FadeOut();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
177
osu.Game/Overlays/Mods/ModSelectFooterContent.cs
Normal file
177
osu.Game/Overlays/Mods/ModSelectFooterContent.cs
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Mods
|
||||||
|
{
|
||||||
|
public partial class ModSelectFooterContent : VisibilityContainer
|
||||||
|
{
|
||||||
|
private readonly ModSelectOverlay overlay;
|
||||||
|
|
||||||
|
private RankingInformationDisplay? rankingInformationDisplay;
|
||||||
|
private BeatmapAttributesDisplay? beatmapAttributesDisplay;
|
||||||
|
private FillFlowContainer<ShearedButton> buttonFlow = null!;
|
||||||
|
private FillFlowContainer contentFlow = null!;
|
||||||
|
|
||||||
|
public DeselectAllModsButton? DeselectAllModsButton { get; set; }
|
||||||
|
|
||||||
|
public readonly IBindable<WorkingBeatmap?> Beatmap = new Bindable<WorkingBeatmap?>();
|
||||||
|
public readonly IBindable<IReadOnlyList<Mod>> ActiveMods = new Bindable<IReadOnlyList<Mod>>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the effects (on score multiplier, on or beatmap difficulty) of the current selected set of mods should be shown.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual bool ShowModEffects => true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the ranking information and beatmap attributes displays are stacked vertically due to small space.
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplaysStackedVertically { get; private set; }
|
||||||
|
|
||||||
|
public ModSelectFooterContent(ModSelectOverlay overlay)
|
||||||
|
{
|
||||||
|
this.overlay = overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = buttonFlow = new FillFlowContainer<ShearedButton>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Padding = new MarginPadding { Horizontal = 20 },
|
||||||
|
Spacing = new Vector2(10),
|
||||||
|
ChildrenEnumerable = CreateButtons(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ShowModEffects)
|
||||||
|
{
|
||||||
|
AddInternal(contentFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(30, 10),
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
Margin = new MarginPadding { Horizontal = 20 },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
rankingInformationDisplay = new RankingInformationDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight
|
||||||
|
},
|
||||||
|
beatmapAttributesDisplay = new BeatmapAttributesDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
BeatmapInfo = { Value = Beatmap.Value?.BeatmapInfo },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModSettingChangeTracker? modSettingChangeTracker;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Beatmap.BindValueChanged(b =>
|
||||||
|
{
|
||||||
|
if (beatmapAttributesDisplay != null)
|
||||||
|
beatmapAttributesDisplay.BeatmapInfo.Value = b.NewValue?.BeatmapInfo;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
ActiveMods.BindValueChanged(m =>
|
||||||
|
{
|
||||||
|
updateInformation();
|
||||||
|
|
||||||
|
modSettingChangeTracker?.Dispose();
|
||||||
|
|
||||||
|
// Importantly, use ActiveMods.Value here (and not the ValueChanged NewValue) as the latter can
|
||||||
|
// potentially be stale, due to complexities in the way change trackers work.
|
||||||
|
//
|
||||||
|
// See https://github.com/ppy/osu/pull/23284#issuecomment-1529056988
|
||||||
|
modSettingChangeTracker = new ModSettingChangeTracker(ActiveMods.Value);
|
||||||
|
modSettingChangeTracker.SettingChanged += _ => updateInformation();
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateInformation()
|
||||||
|
{
|
||||||
|
if (rankingInformationDisplay != null)
|
||||||
|
{
|
||||||
|
double multiplier = 1.0;
|
||||||
|
|
||||||
|
foreach (var mod in ActiveMods.Value)
|
||||||
|
multiplier *= mod.ScoreMultiplier;
|
||||||
|
|
||||||
|
rankingInformationDisplay.ModMultiplier.Value = multiplier;
|
||||||
|
rankingInformationDisplay.Ranked.Value = ActiveMods.Value.All(m => m.Ranked);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (beatmapAttributesDisplay != null)
|
||||||
|
beatmapAttributesDisplay.Mods.Value = ActiveMods.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (beatmapAttributesDisplay != null)
|
||||||
|
{
|
||||||
|
float rightEdgeOfLastButton = buttonFlow[^1].ScreenSpaceDrawQuad.TopRight.X;
|
||||||
|
|
||||||
|
// this is cheating a bit; the 640 value is hardcoded based on how wide the expanded panel _generally_ is.
|
||||||
|
// due to the transition applied, the raw screenspace quad of the panel cannot be used, as it will trigger an ugly feedback cycle of expanding and collapsing.
|
||||||
|
float projectedLeftEdgeOfExpandedBeatmapAttributesDisplay = buttonFlow.ToScreenSpace(buttonFlow.DrawSize - new Vector2(640, 0)).X;
|
||||||
|
|
||||||
|
DisplaysStackedVertically = rightEdgeOfLastButton > projectedLeftEdgeOfExpandedBeatmapAttributesDisplay;
|
||||||
|
|
||||||
|
// only update preview panel's collapsed state after we are fully visible, to ensure all the buttons are where we expect them to be.
|
||||||
|
if (Alpha == 1)
|
||||||
|
beatmapAttributesDisplay.Collapsed.Value = DisplaysStackedVertically;
|
||||||
|
|
||||||
|
contentFlow.LayoutDuration = 200;
|
||||||
|
contentFlow.LayoutEasing = Easing.OutQuint;
|
||||||
|
contentFlow.Direction = DisplaysStackedVertically ? FillDirection.Vertical : FillDirection.Horizontal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual IEnumerable<ShearedButton> CreateButtons() => new[]
|
||||||
|
{
|
||||||
|
DeselectAllModsButton = new DeselectAllModsButton(overlay)
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
this.MoveToY(0, 400, Easing.OutQuint)
|
||||||
|
.FadeIn(400, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
this.MoveToY(-20f, 200, Easing.OutQuint)
|
||||||
|
.FadeOut(200, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -87,11 +88,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
public ShearedSearchTextBox SearchTextBox { get; private set; } = null!;
|
public ShearedSearchTextBox SearchTextBox { get; private set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the effects (on score multiplier, on or beatmap difficulty) of the current selected set of mods should be shown.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual bool ShowModEffects => true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether per-mod customisation controls are visible.
|
/// Whether per-mod customisation controls are visible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -108,11 +104,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
protected virtual IReadOnlyList<Mod> ComputeActiveMods() => SelectedMods.Value;
|
protected virtual IReadOnlyList<Mod> ComputeActiveMods() => SelectedMods.Value;
|
||||||
|
|
||||||
protected virtual IEnumerable<ShearedButton> CreateFooterButtons()
|
|
||||||
{
|
|
||||||
yield return deselectAllModsButton = new DeselectAllModsButton(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> globalAvailableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> globalAvailableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
||||||
|
|
||||||
public IEnumerable<ModState> AllAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value);
|
public IEnumerable<ModState> AllAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value);
|
||||||
@ -121,34 +112,18 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private ColumnScrollContainer columnScroll = null!;
|
private ColumnScrollContainer columnScroll = null!;
|
||||||
private ColumnFlowContainer columnFlow = null!;
|
private ColumnFlowContainer columnFlow = null!;
|
||||||
private FillFlowContainer<ShearedButton> footerButtonFlow = null!;
|
|
||||||
private FillFlowContainer footerContentFlow = null!;
|
|
||||||
private DeselectAllModsButton deselectAllModsButton = null!;
|
|
||||||
|
|
||||||
private Container aboveColumnsContent = null!;
|
private Container aboveColumnsContent = null!;
|
||||||
private RankingInformationDisplay? rankingInformationDisplay;
|
|
||||||
private BeatmapAttributesDisplay? beatmapAttributesDisplay;
|
|
||||||
private ModCustomisationPanel customisationPanel = null!;
|
private ModCustomisationPanel customisationPanel = null!;
|
||||||
|
|
||||||
protected ShearedButton BackButton { get; private set; } = null!;
|
protected virtual SelectAllModsButton? SelectAllModsButton => null;
|
||||||
protected SelectAllModsButton? SelectAllModsButton { get; set; }
|
|
||||||
|
|
||||||
private Sample? columnAppearSample;
|
private Sample? columnAppearSample;
|
||||||
|
|
||||||
private WorkingBeatmap? beatmap;
|
public readonly Bindable<WorkingBeatmap?> Beatmap = new Bindable<WorkingBeatmap?>();
|
||||||
|
|
||||||
public WorkingBeatmap? Beatmap
|
[Resolved]
|
||||||
{
|
private ScreenFooter? footer { get; set; }
|
||||||
get => beatmap;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (beatmap == value) return;
|
|
||||||
|
|
||||||
beatmap = value;
|
|
||||||
if (IsLoaded && beatmapAttributesDisplay != null)
|
|
||||||
beatmapAttributesDisplay.BeatmapInfo.Value = beatmap?.BeatmapInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green)
|
protected ModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green)
|
||||||
: base(colourScheme)
|
: base(colourScheme)
|
||||||
@ -227,59 +202,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
FooterContent.Add(footerButtonFlow = new FillFlowContainer<ShearedButton>
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Padding = new MarginPadding
|
|
||||||
{
|
|
||||||
Vertical = PADDING,
|
|
||||||
Horizontal = 70
|
|
||||||
},
|
|
||||||
Spacing = new Vector2(10),
|
|
||||||
ChildrenEnumerable = CreateFooterButtons().Prepend(BackButton = new ShearedButton(BUTTON_WIDTH)
|
|
||||||
{
|
|
||||||
Text = CommonStrings.Back,
|
|
||||||
Action = Hide,
|
|
||||||
DarkerColour = colours.Pink2,
|
|
||||||
LighterColour = colours.Pink1
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if (ShowModEffects)
|
|
||||||
{
|
|
||||||
FooterContent.Add(footerContentFlow = new FillFlowContainer
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Spacing = new Vector2(30, 10),
|
|
||||||
Anchor = Anchor.BottomRight,
|
|
||||||
Origin = Anchor.BottomRight,
|
|
||||||
Margin = new MarginPadding
|
|
||||||
{
|
|
||||||
Vertical = PADDING,
|
|
||||||
Horizontal = 20
|
|
||||||
},
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
rankingInformationDisplay = new RankingInformationDisplay
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomRight,
|
|
||||||
Origin = Anchor.BottomRight
|
|
||||||
},
|
|
||||||
beatmapAttributesDisplay = new BeatmapAttributesDisplay
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomRight,
|
|
||||||
Origin = Anchor.BottomRight,
|
|
||||||
BeatmapInfo = { Value = Beatmap?.BeatmapInfo },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
globalAvailableMods.BindTo(game.AvailableMods);
|
globalAvailableMods.BindTo(game.AvailableMods);
|
||||||
|
|
||||||
textSearchStartsActive = configManager.GetBindable<bool>(OsuSetting.ModSelectTextSearchStartsActive);
|
textSearchStartsActive = configManager.GetBindable<bool>(OsuSetting.ModSelectTextSearchStartsActive);
|
||||||
@ -293,8 +215,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
SearchTextBox.Current.Value = string.Empty;
|
SearchTextBox.Current.Value = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModSettingChangeTracker? modSettingChangeTracker;
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
// this is called before base call so that the mod state is populated early, and the transition in `PopIn()` can play out properly.
|
// this is called before base call so that the mod state is populated early, and the transition in `PopIn()` can play out properly.
|
||||||
@ -317,23 +237,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
ActiveMods.Value = ComputeActiveMods();
|
ActiveMods.Value = ComputeActiveMods();
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
ActiveMods.BindValueChanged(_ =>
|
|
||||||
{
|
|
||||||
updateOverlayInformation();
|
|
||||||
|
|
||||||
modSettingChangeTracker?.Dispose();
|
|
||||||
|
|
||||||
if (AllowCustomisation)
|
|
||||||
{
|
|
||||||
// Importantly, use ActiveMods.Value here (and not the ValueChanged NewValue) as the latter can
|
|
||||||
// potentially be stale, due to complexities in the way change trackers work.
|
|
||||||
//
|
|
||||||
// See https://github.com/ppy/osu/pull/23284#issuecomment-1529056988
|
|
||||||
modSettingChangeTracker = new ModSettingChangeTracker(ActiveMods.Value);
|
|
||||||
modSettingChangeTracker.SettingChanged += _ => updateOverlayInformation();
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
customisationPanel.Expanded.BindValueChanged(_ => updateCustomisationVisualState(), true);
|
customisationPanel.Expanded.BindValueChanged(_ => updateCustomisationVisualState(), true);
|
||||||
|
|
||||||
SearchTextBox.Current.BindValueChanged(query =>
|
SearchTextBox.Current.BindValueChanged(query =>
|
||||||
@ -351,6 +254,14 @@ namespace osu.Game.Overlays.Mods
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public new ModSelectFooterContent? DisplayedFooterContent => base.DisplayedFooterContent as ModSelectFooterContent;
|
||||||
|
|
||||||
|
public override VisibilityContainer CreateFooterContent() => new ModSelectFooterContent(this)
|
||||||
|
{
|
||||||
|
Beatmap = { BindTarget = Beatmap },
|
||||||
|
ActiveMods = { BindTarget = ActiveMods },
|
||||||
|
};
|
||||||
|
|
||||||
private static readonly LocalisableString input_search_placeholder = Resources.Localisation.Web.CommonStrings.InputSearch;
|
private static readonly LocalisableString input_search_placeholder = Resources.Localisation.Web.CommonStrings.InputSearch;
|
||||||
private static readonly LocalisableString tab_to_search_placeholder = ModSelectOverlayStrings.TabToSearch;
|
private static readonly LocalisableString tab_to_search_placeholder = ModSelectOverlayStrings.TabToSearch;
|
||||||
|
|
||||||
@ -359,26 +270,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
SearchTextBox.PlaceholderText = SearchTextBox.HasFocus ? input_search_placeholder : tab_to_search_placeholder;
|
SearchTextBox.PlaceholderText = SearchTextBox.HasFocus ? input_search_placeholder : tab_to_search_placeholder;
|
||||||
|
aboveColumnsContent.Padding = aboveColumnsContent.Padding with { Bottom = DisplayedFooterContent?.DisplaysStackedVertically == true ? 75f : 15f };
|
||||||
if (beatmapAttributesDisplay != null)
|
|
||||||
{
|
|
||||||
float rightEdgeOfLastButton = footerButtonFlow[^1].ScreenSpaceDrawQuad.TopRight.X;
|
|
||||||
|
|
||||||
// this is cheating a bit; the 640 value is hardcoded based on how wide the expanded panel _generally_ is.
|
|
||||||
// due to the transition applied, the raw screenspace quad of the panel cannot be used, as it will trigger an ugly feedback cycle of expanding and collapsing.
|
|
||||||
float projectedLeftEdgeOfExpandedBeatmapAttributesDisplay = footerButtonFlow.ToScreenSpace(footerButtonFlow.DrawSize - new Vector2(640, 0)).X;
|
|
||||||
|
|
||||||
bool screenIsntWideEnough = rightEdgeOfLastButton > projectedLeftEdgeOfExpandedBeatmapAttributesDisplay;
|
|
||||||
|
|
||||||
// only update preview panel's collapsed state after we are fully visible, to ensure all the buttons are where we expect them to be.
|
|
||||||
if (Alpha == 1)
|
|
||||||
beatmapAttributesDisplay.Collapsed.Value = screenIsntWideEnough;
|
|
||||||
|
|
||||||
footerContentFlow.LayoutDuration = 200;
|
|
||||||
footerContentFlow.LayoutEasing = Easing.OutQuint;
|
|
||||||
footerContentFlow.Direction = screenIsntWideEnough ? FillDirection.Vertical : FillDirection.Horizontal;
|
|
||||||
aboveColumnsContent.Padding = aboveColumnsContent.Padding with { Bottom = screenIsntWideEnough ? 70f : 15f };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -456,27 +348,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
modState.ValidForSelection.Value = modState.Mod.Type != ModType.System && modState.Mod.HasImplementation && IsValidMod.Invoke(modState.Mod);
|
modState.ValidForSelection.Value = modState.Mod.Type != ModType.System && modState.Mod.HasImplementation && IsValidMod.Invoke(modState.Mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates any information displayed on the overlay regarding the effects of the active mods.
|
|
||||||
/// This reads from <see cref="ActiveMods"/> instead of <see cref="SelectedMods"/>.
|
|
||||||
/// </summary>
|
|
||||||
private void updateOverlayInformation()
|
|
||||||
{
|
|
||||||
if (rankingInformationDisplay != null)
|
|
||||||
{
|
|
||||||
double multiplier = 1.0;
|
|
||||||
|
|
||||||
foreach (var mod in ActiveMods.Value)
|
|
||||||
multiplier *= mod.ScoreMultiplier;
|
|
||||||
|
|
||||||
rankingInformationDisplay.ModMultiplier.Value = multiplier;
|
|
||||||
rankingInformationDisplay.Ranked.Value = ActiveMods.Value.All(m => m.Ranked);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (beatmapAttributesDisplay != null)
|
|
||||||
beatmapAttributesDisplay.Mods.Value = ActiveMods.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateCustomisation()
|
private void updateCustomisation()
|
||||||
{
|
{
|
||||||
if (!AllowCustomisation)
|
if (!AllowCustomisation)
|
||||||
@ -702,7 +573,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
if (!SearchTextBox.HasFocus && !customisationPanel.Expanded.Value)
|
if (!SearchTextBox.HasFocus && !customisationPanel.Expanded.Value)
|
||||||
{
|
{
|
||||||
deselectAllModsButton.TriggerClick();
|
DisplayedFooterContent?.DeselectAllModsButton?.TriggerClick();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,7 +604,13 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
return base.OnPressed(e);
|
return base.OnPressed(e);
|
||||||
|
|
||||||
void hideOverlay() => BackButton.TriggerClick();
|
void hideOverlay()
|
||||||
|
{
|
||||||
|
if (footer != null)
|
||||||
|
footer.BackButton.TriggerClick();
|
||||||
|
else
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IKeyBindingHandler{PlatformAction}"/>
|
/// <inheritdoc cref="IKeyBindingHandler{PlatformAction}"/>
|
||||||
@ -741,7 +618,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
/// This is handled locally here due to conflicts in input handling between the search text box and the select all mods button.
|
/// This is handled locally here due to conflicts in input handling between the search text box and the select all mods button.
|
||||||
/// Attempting to handle this action locally in both places leads to a possible scenario
|
/// Attempting to handle this action locally in both places leads to a possible scenario
|
||||||
/// wherein activating the "select all" platform binding will both select all text in the search box and select all mods.
|
/// wherein activating the "select all" platform binding will both select all text in the search box and select all mods.
|
||||||
/// </remarks>>
|
/// </remarks>
|
||||||
public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
||||||
{
|
{
|
||||||
if (e.Repeat || e.Action != PlatformAction.SelectAll || SelectAllModsButton == null)
|
if (e.Repeat || e.Action != PlatformAction.SelectAll || SelectAllModsButton == null)
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// 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.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
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.Screens.Footer;
|
using osu.Game.Screens.Footer;
|
||||||
@ -37,9 +38,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private ScreenFooter? footer { get; set; }
|
private ScreenFooter? footer { get; set; }
|
||||||
|
|
||||||
// todo: very temporary property that will be removed once ModSelectOverlay and FirstRunSetupOverlay are updated to use new footer.
|
|
||||||
public virtual bool UseNewFooter => false;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A container containing all content, including the header and footer.
|
/// A container containing all content, including the header and footer.
|
||||||
/// May be used for overlay-wide animations.
|
/// May be used for overlay-wide animations.
|
||||||
@ -60,6 +58,10 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
protected override bool BlockNonPositionalInput => true;
|
protected override bool BlockNonPositionalInput => true;
|
||||||
|
|
||||||
|
// ShearedOverlayContainers are placed at a layer within the screen container as they rely on ScreenFooter which must be placed there.
|
||||||
|
// Therefore, dimming must be managed locally, since DimMainContent dims the entire screen layer.
|
||||||
|
protected sealed override bool DimMainContent => false;
|
||||||
|
|
||||||
protected ShearedOverlayContainer(OverlayColourScheme colourScheme)
|
protected ShearedOverlayContainer(OverlayColourScheme colourScheme)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
@ -70,13 +72,16 @@ namespace osu.Game.Overlays.Mods
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
const float footer_height = ScreenFooter.HEIGHT;
|
|
||||||
|
|
||||||
Child = TopLevelContent = new Container
|
Child = TopLevelContent = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = ColourProvider.Background6.Opacity(0.75f),
|
||||||
|
},
|
||||||
Header = new ShearedOverlayHeader
|
Header = new ShearedOverlayHeader
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
@ -90,38 +95,19 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Top = ShearedOverlayHeader.HEIGHT,
|
Top = ShearedOverlayHeader.HEIGHT,
|
||||||
Bottom = footer_height + PADDING,
|
Bottom = ScreenFooter.HEIGHT + PADDING,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Footer = new InputBlockingContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Depth = float.MinValue,
|
|
||||||
Height = footer_height,
|
|
||||||
Margin = new MarginPadding { Top = PADDING },
|
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
Origin = Anchor.BottomCentre,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = ColourProvider.Background5
|
|
||||||
},
|
|
||||||
FooterContent = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VisibilityContainer? DisplayedFooterContent { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates content to be displayed on the game-wide footer.
|
/// Creates content to be displayed on the game-wide footer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual Drawable CreateFooterContent() => Empty();
|
public virtual VisibilityContainer? CreateFooterContent() => null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when the back button in the footer is pressed.
|
/// Invoked when the back button in the footer is pressed.
|
||||||
@ -140,6 +126,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
return base.OnClick(e);
|
return base.OnClick(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IDisposable? activeOverlayRegistration;
|
||||||
private bool hideFooterOnPopOut;
|
private bool hideFooterOnPopOut;
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
@ -150,9 +137,10 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
Header.MoveToY(0, fade_in_duration, Easing.OutQuint);
|
Header.MoveToY(0, fade_in_duration, Easing.OutQuint);
|
||||||
|
|
||||||
if (UseNewFooter && footer != null)
|
if (footer != null)
|
||||||
{
|
{
|
||||||
footer.SetActiveOverlayContainer(this);
|
activeOverlayRegistration = footer.RegisterActiveOverlayContainer(this, out var footerContent);
|
||||||
|
DisplayedFooterContent = footerContent;
|
||||||
|
|
||||||
if (footer.State.Value == Visibility.Hidden)
|
if (footer.State.Value == Visibility.Hidden)
|
||||||
{
|
{
|
||||||
@ -160,8 +148,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
hideFooterOnPopOut = true;
|
hideFooterOnPopOut = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
Footer.MoveToY(0, fade_in_duration, Easing.OutQuint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
@ -173,9 +159,11 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
Header.MoveToY(-Header.DrawHeight, fade_out_duration, Easing.OutQuint);
|
Header.MoveToY(-Header.DrawHeight, fade_out_duration, Easing.OutQuint);
|
||||||
|
|
||||||
if (UseNewFooter && footer != null)
|
if (footer != null)
|
||||||
{
|
{
|
||||||
footer.ClearActiveOverlayContainer();
|
activeOverlayRegistration?.Dispose();
|
||||||
|
activeOverlayRegistration = null;
|
||||||
|
DisplayedFooterContent = null;
|
||||||
|
|
||||||
if (hideFooterOnPopOut)
|
if (hideFooterOnPopOut)
|
||||||
{
|
{
|
||||||
@ -183,8 +171,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
hideFooterOnPopOut = false;
|
hideFooterOnPopOut = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
Footer.MoveToY(Footer.DrawHeight, fade_out_duration, Easing.OutQuint);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
@ -24,6 +25,7 @@ namespace osu.Game.Screens.Footer
|
|||||||
{
|
{
|
||||||
private const int padding = 60;
|
private const int padding = 60;
|
||||||
private const float delay_per_button = 30;
|
private const float delay_per_button = 30;
|
||||||
|
private const double transition_duration = 400;
|
||||||
|
|
||||||
public const int HEIGHT = 50;
|
public const int HEIGHT = 50;
|
||||||
|
|
||||||
@ -37,8 +39,13 @@ namespace osu.Game.Screens.Footer
|
|||||||
[Cached]
|
[Cached]
|
||||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuGame? game { get; set; }
|
||||||
|
|
||||||
public ScreenBackButton BackButton { get; private set; } = null!;
|
public ScreenBackButton BackButton { get; private set; } = null!;
|
||||||
|
|
||||||
|
public Action<bool>? RequestLogoInFront { get; set; }
|
||||||
|
|
||||||
public Action? OnBack;
|
public Action? OnBack;
|
||||||
|
|
||||||
public ScreenFooter(BackReceptor? receptor = null)
|
public ScreenFooter(BackReceptor? receptor = null)
|
||||||
@ -101,19 +108,35 @@ namespace osu.Game.Screens.Footer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartTrackingLogo(OsuLogo logo, float duration = 0, Easing easing = Easing.None) => logoTrackingContainer.StartTracking(logo, duration, easing);
|
private ScheduledDelegate? changeLogoDepthDelegate;
|
||||||
public void StopTrackingLogo() => logoTrackingContainer.StopTracking();
|
|
||||||
|
public void StartTrackingLogo(OsuLogo logo, float duration = 0, Easing easing = Easing.None)
|
||||||
|
{
|
||||||
|
changeLogoDepthDelegate?.Cancel();
|
||||||
|
changeLogoDepthDelegate = null;
|
||||||
|
|
||||||
|
logoTrackingContainer.StartTracking(logo, duration, easing);
|
||||||
|
RequestLogoInFront?.Invoke(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopTrackingLogo()
|
||||||
|
{
|
||||||
|
logoTrackingContainer.StopTracking();
|
||||||
|
|
||||||
|
if (game != null)
|
||||||
|
changeLogoDepthDelegate = Scheduler.AddDelayed(() => RequestLogoInFront?.Invoke(false), transition_duration);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
{
|
{
|
||||||
this.MoveToY(0, 400, Easing.OutQuint)
|
this.MoveToY(0, transition_duration, Easing.OutQuint)
|
||||||
.FadeIn(400, Easing.OutQuint);
|
.FadeIn(transition_duration, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
{
|
{
|
||||||
this.MoveToY(HEIGHT, 400, Easing.OutQuint)
|
this.MoveToY(HEIGHT, transition_duration, Easing.OutQuint)
|
||||||
.FadeOut(400, Easing.OutQuint);
|
.FadeOut(transition_duration, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetButtons(IReadOnlyList<ScreenFooterButton> buttons)
|
public void SetButtons(IReadOnlyList<ScreenFooterButton> buttons)
|
||||||
@ -121,7 +144,7 @@ namespace osu.Game.Screens.Footer
|
|||||||
temporarilyHiddenButtons.Clear();
|
temporarilyHiddenButtons.Clear();
|
||||||
overlays.Clear();
|
overlays.Clear();
|
||||||
|
|
||||||
ClearActiveOverlayContainer();
|
clearActiveOverlayContainer();
|
||||||
|
|
||||||
var oldButtons = buttonsFlow.ToArray();
|
var oldButtons = buttonsFlow.ToArray();
|
||||||
|
|
||||||
@ -166,14 +189,15 @@ namespace osu.Game.Screens.Footer
|
|||||||
|
|
||||||
private ShearedOverlayContainer? activeOverlay;
|
private ShearedOverlayContainer? activeOverlay;
|
||||||
private Container? contentContainer;
|
private Container? contentContainer;
|
||||||
|
|
||||||
private readonly List<ScreenFooterButton> temporarilyHiddenButtons = new List<ScreenFooterButton>();
|
private readonly List<ScreenFooterButton> temporarilyHiddenButtons = new List<ScreenFooterButton>();
|
||||||
|
|
||||||
public void SetActiveOverlayContainer(ShearedOverlayContainer overlay)
|
public IDisposable RegisterActiveOverlayContainer(ShearedOverlayContainer overlay, out VisibilityContainer? footerContent)
|
||||||
{
|
{
|
||||||
if (contentContainer != null)
|
if (activeOverlay != null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(@"Cannot set overlay content while one is already present. " +
|
throw new InvalidOperationException(@"Cannot set overlay content while one is already present. " +
|
||||||
$@"The previous overlay whose content is {contentContainer.Child.GetType().Name} should be hidden first.");
|
$@"The previous overlay ({activeOverlay.GetType().Name}) should be hidden first.");
|
||||||
}
|
}
|
||||||
|
|
||||||
activeOverlay = overlay;
|
activeOverlay = overlay;
|
||||||
@ -197,7 +221,9 @@ namespace osu.Game.Screens.Footer
|
|||||||
|
|
||||||
updateColourScheme(overlay.ColourProvider.ColourScheme);
|
updateColourScheme(overlay.ColourProvider.ColourScheme);
|
||||||
|
|
||||||
var content = overlay.CreateFooterContent();
|
footerContent = overlay.CreateFooterContent();
|
||||||
|
|
||||||
|
var content = footerContent ?? Empty();
|
||||||
|
|
||||||
Add(contentContainer = new Container
|
Add(contentContainer = new Container
|
||||||
{
|
{
|
||||||
@ -211,29 +237,30 @@ namespace osu.Game.Screens.Footer
|
|||||||
this.Delay(60).Schedule(() => content.Show());
|
this.Delay(60).Schedule(() => content.Show());
|
||||||
else
|
else
|
||||||
content.Show();
|
content.Show();
|
||||||
|
|
||||||
|
return new InvokeOnDisposal(clearActiveOverlayContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearActiveOverlayContainer()
|
private void clearActiveOverlayContainer()
|
||||||
{
|
{
|
||||||
if (contentContainer == null)
|
if (activeOverlay == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Debug.Assert(contentContainer != null);
|
||||||
contentContainer.Child.Hide();
|
contentContainer.Child.Hide();
|
||||||
|
|
||||||
double timeUntilRun = contentContainer.Child.LatestTransformEndTime - Time.Current;
|
double timeUntilRun = contentContainer.Child.LatestTransformEndTime - Time.Current;
|
||||||
|
|
||||||
Container expireTarget = contentContainer;
|
|
||||||
contentContainer = null;
|
|
||||||
activeOverlay = null;
|
|
||||||
|
|
||||||
for (int i = 0; i < temporarilyHiddenButtons.Count; i++)
|
for (int i = 0; i < temporarilyHiddenButtons.Count; i++)
|
||||||
makeButtonAppearFromBottom(temporarilyHiddenButtons[i], 0);
|
makeButtonAppearFromBottom(temporarilyHiddenButtons[i], 0);
|
||||||
|
|
||||||
temporarilyHiddenButtons.Clear();
|
temporarilyHiddenButtons.Clear();
|
||||||
|
|
||||||
expireTarget.Delay(timeUntilRun).Expire();
|
|
||||||
|
|
||||||
updateColourScheme(OverlayColourScheme.Aquamarine);
|
updateColourScheme(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
|
contentContainer.Delay(timeUntilRun).Expire();
|
||||||
|
contentContainer = null;
|
||||||
|
activeOverlay = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateColourScheme(OverlayColourScheme colourScheme)
|
private void updateColourScheme(OverlayColourScheme colourScheme)
|
||||||
|
@ -6,6 +6,7 @@ using osu.Game.Overlays;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -14,8 +15,6 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
{
|
{
|
||||||
public partial class FreeModSelectOverlay : ModSelectOverlay
|
public partial class FreeModSelectOverlay : ModSelectOverlay
|
||||||
{
|
{
|
||||||
protected override bool ShowModEffects => false;
|
|
||||||
|
|
||||||
protected override bool AllowCustomisation => false;
|
protected override bool AllowCustomisation => false;
|
||||||
|
|
||||||
public new Func<Mod, bool> IsValidMod
|
public new Func<Mod, bool> IsValidMod
|
||||||
@ -24,6 +23,8 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
set => base.IsValidMod = m => m.UserPlayable && value.Invoke(m);
|
set => base.IsValidMod = m => m.UserPlayable && value.Invoke(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override SelectAllModsButton? SelectAllModsButton => DisplayedFooterContent?.SelectAllModsButton;
|
||||||
|
|
||||||
public FreeModSelectOverlay()
|
public FreeModSelectOverlay()
|
||||||
: base(OverlayColourScheme.Plum)
|
: base(OverlayColourScheme.Plum)
|
||||||
{
|
{
|
||||||
@ -32,12 +33,35 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
|
|
||||||
protected override ModColumn CreateModColumn(ModType modType) => new ModColumn(modType, true);
|
protected override ModColumn CreateModColumn(ModType modType) => new ModColumn(modType, true);
|
||||||
|
|
||||||
protected override IEnumerable<ShearedButton> CreateFooterButtons()
|
public new FreeModSelectFooterContent? DisplayedFooterContent => base.DisplayedFooterContent as FreeModSelectFooterContent;
|
||||||
=> base.CreateFooterButtons()
|
|
||||||
.Prepend(SelectAllModsButton = new SelectAllModsButton(this)
|
public override VisibilityContainer CreateFooterContent() => new FreeModSelectFooterContent(this)
|
||||||
|
{
|
||||||
|
Beatmap = { BindTarget = Beatmap },
|
||||||
|
ActiveMods = { BindTarget = ActiveMods },
|
||||||
|
};
|
||||||
|
|
||||||
|
public partial class FreeModSelectFooterContent : ModSelectFooterContent
|
||||||
|
{
|
||||||
|
private readonly FreeModSelectOverlay overlay;
|
||||||
|
|
||||||
|
protected override bool ShowModEffects => false;
|
||||||
|
|
||||||
|
public SelectAllModsButton? SelectAllModsButton;
|
||||||
|
|
||||||
|
public FreeModSelectFooterContent(FreeModSelectOverlay overlay)
|
||||||
|
: base(overlay)
|
||||||
|
{
|
||||||
|
this.overlay = overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<ShearedButton> CreateButtons()
|
||||||
|
=> base.CreateButtons()
|
||||||
|
.Prepend(SelectAllModsButton = new SelectAllModsButton(overlay)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -456,7 +456,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
// Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
|
// Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
|
||||||
var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineID == beatmap.OnlineID);
|
var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineID == beatmap.OnlineID);
|
||||||
|
|
||||||
UserModsSelectOverlay.Beatmap = Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
UserModsSelectOverlay.Beatmap.Value = Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void UpdateMods()
|
protected virtual void UpdateMods()
|
||||||
|
@ -851,7 +851,7 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
BeatmapDetails.Beatmap = beatmap;
|
BeatmapDetails.Beatmap = beatmap;
|
||||||
|
|
||||||
ModSelect.Beatmap = beatmap;
|
ModSelect.Beatmap.Value = beatmap;
|
||||||
|
|
||||||
advancedStats.BeatmapInfo = beatmap.BeatmapInfo;
|
advancedStats.BeatmapInfo = beatmap.BeatmapInfo;
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
@ -23,6 +22,8 @@ namespace osu.Game.Screens.SelectV2
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class SongSelectV2 : OsuScreen
|
public partial class SongSelectV2 : OsuScreen
|
||||||
{
|
{
|
||||||
|
private const float logo_scale = 0.4f;
|
||||||
|
|
||||||
private readonly ModSelectOverlay modSelectOverlay = new SoloModSelectOverlay();
|
private readonly ModSelectOverlay modSelectOverlay = new SoloModSelectOverlay();
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
@ -30,15 +31,14 @@ namespace osu.Game.Screens.SelectV2
|
|||||||
|
|
||||||
public override bool ShowFooter => true;
|
public override bool ShowFooter => true;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuLogo? logo { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
new PopoverContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
modSelectOverlay,
|
modSelectOverlay,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -50,21 +50,43 @@ namespace osu.Game.Screens.SelectV2
|
|||||||
new ScreenFooterButtonOptions(),
|
new ScreenFooterButtonOptions(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
modSelectOverlay.State.BindValueChanged(v =>
|
||||||
|
{
|
||||||
|
logo?.ScaleTo(v.NewValue == Visibility.Visible ? 0f : logo_scale, 400, Easing.OutQuint)
|
||||||
|
.FadeTo(v.NewValue == Visibility.Visible ? 0f : 1f, 200, Easing.OutQuint);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnEntering(ScreenTransitionEvent e)
|
public override void OnEntering(ScreenTransitionEvent e)
|
||||||
{
|
{
|
||||||
this.FadeIn();
|
this.FadeIn();
|
||||||
|
|
||||||
|
modSelectOverlay.SelectedMods.BindTo(Mods);
|
||||||
|
|
||||||
base.OnEntering(e);
|
base.OnEntering(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnResuming(ScreenTransitionEvent e)
|
public override void OnResuming(ScreenTransitionEvent e)
|
||||||
{
|
{
|
||||||
this.FadeIn();
|
this.FadeIn();
|
||||||
|
|
||||||
|
// required due to https://github.com/ppy/osu-framework/issues/3218
|
||||||
|
modSelectOverlay.SelectedMods.Disabled = false;
|
||||||
|
modSelectOverlay.SelectedMods.BindTo(Mods);
|
||||||
|
|
||||||
base.OnResuming(e);
|
base.OnResuming(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnSuspending(ScreenTransitionEvent e)
|
public override void OnSuspending(ScreenTransitionEvent e)
|
||||||
{
|
{
|
||||||
this.Delay(400).FadeOut();
|
this.Delay(400).FadeOut();
|
||||||
|
|
||||||
|
modSelectOverlay.SelectedMods.UnbindFrom(Mods);
|
||||||
|
|
||||||
base.OnSuspending(e);
|
base.OnSuspending(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,17 +96,6 @@ namespace osu.Game.Screens.SelectV2
|
|||||||
return base.OnExiting(e);
|
return base.OnExiting(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnBackButton()
|
|
||||||
{
|
|
||||||
if (modSelectOverlay.State.Value == Visibility.Visible)
|
|
||||||
{
|
|
||||||
modSelectOverlay.Hide();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
||||||
{
|
{
|
||||||
base.LogoArriving(logo, resuming);
|
base.LogoArriving(logo, resuming);
|
||||||
@ -99,7 +110,7 @@ namespace osu.Game.Screens.SelectV2
|
|||||||
}
|
}
|
||||||
|
|
||||||
logo.FadeIn(240, Easing.OutQuint);
|
logo.FadeIn(240, Easing.OutQuint);
|
||||||
logo.ScaleTo(0.4f, 240, Easing.OutQuint);
|
logo.ScaleTo(logo_scale, 240, Easing.OutQuint);
|
||||||
|
|
||||||
logo.Action = () =>
|
logo.Action = () =>
|
||||||
{
|
{
|
||||||
@ -122,14 +133,9 @@ namespace osu.Game.Screens.SelectV2
|
|||||||
logo.FadeOut(120, Easing.Out);
|
logo.FadeOut(120, Easing.Out);
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class SoloModSelectOverlay : ModSelectOverlay
|
private partial class SoloModSelectOverlay : UserModSelectOverlay
|
||||||
{
|
{
|
||||||
protected override bool ShowPresets => true;
|
protected override bool ShowPresets => true;
|
||||||
|
|
||||||
public SoloModSelectOverlay()
|
|
||||||
: base(OverlayColourScheme.Aquamarine)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class PlayerLoaderV2 : PlayerLoader
|
private partial class PlayerLoaderV2 : PlayerLoader
|
||||||
|
@ -658,7 +658,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private partial class TestModSelectOverlay : UserModSelectOverlay
|
private partial class TestModSelectOverlay : UserModSelectOverlay
|
||||||
{
|
{
|
||||||
protected override bool ShowModEffects => true;
|
|
||||||
protected override bool ShowPresets => false;
|
protected override bool ShowPresets => false;
|
||||||
|
|
||||||
public TestModSelectOverlay()
|
public TestModSelectOverlay()
|
||||||
|
@ -12,6 +12,7 @@ using osu.Framework.Testing;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual
|
namespace osu.Game.Tests.Visual
|
||||||
{
|
{
|
||||||
@ -30,6 +31,9 @@ namespace osu.Game.Tests.Visual
|
|||||||
[Cached(typeof(IDialogOverlay))]
|
[Cached(typeof(IDialogOverlay))]
|
||||||
protected DialogOverlay DialogOverlay { get; private set; }
|
protected DialogOverlay DialogOverlay { get; private set; }
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private ScreenFooter footer;
|
||||||
|
|
||||||
protected ScreenTestScene()
|
protected ScreenTestScene()
|
||||||
{
|
{
|
||||||
base.Content.AddRange(new Drawable[]
|
base.Content.AddRange(new Drawable[]
|
||||||
@ -44,7 +48,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = DialogOverlay = new DialogOverlay()
|
Child = DialogOverlay = new DialogOverlay()
|
||||||
}
|
},
|
||||||
|
footer = new ScreenFooter(),
|
||||||
});
|
});
|
||||||
|
|
||||||
Stack.ScreenPushed += (_, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed → {newScreen}");
|
Stack.ScreenPushed += (_, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed → {newScreen}");
|
||||||
|
Loading…
Reference in New Issue
Block a user