mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 03:25:11 +08:00
Merge pull request #18130 from bdach/mod-overlay/bulk-select-buttons
Add back select/deselect all mods buttons to new mod select design
This commit is contained in:
commit
2752bdf04f
@ -1,19 +1,32 @@
|
|||||||
// 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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
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.Screens.OnlinePlay;
|
using osu.Game.Screens.OnlinePlay;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Multiplayer
|
namespace osu.Game.Tests.Visual.Multiplayer
|
||||||
{
|
{
|
||||||
public class TestSceneFreeModSelectScreen : MultiplayerTestScene
|
public class TestSceneFreeModSelectScreen : MultiplayerTestScene
|
||||||
{
|
{
|
||||||
private FreeModSelectScreen freeModSelectScreen;
|
private FreeModSelectScreen freeModSelectScreen;
|
||||||
|
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> availableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuGameBase osuGameBase)
|
||||||
|
{
|
||||||
|
availableMods.BindTo(osuGameBase.AvailableMods);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestFreeModSelect()
|
public void TestFreeModSelect()
|
||||||
@ -42,6 +55,26 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddAssert("customisation area not expanded", () => this.ChildrenOfType<ModSettingsArea>().Single().Height == 0);
|
AddAssert("customisation area not expanded", () => this.ChildrenOfType<ModSettingsArea>().Single().Height == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSelectDeselectAll()
|
||||||
|
{
|
||||||
|
createFreeModSelect();
|
||||||
|
|
||||||
|
AddStep("click select all button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().First());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("all mods selected", assertAllAvailableModsSelected);
|
||||||
|
|
||||||
|
AddStep("click deselect all button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().Last());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("all mods deselected", () => !freeModSelectScreen.SelectedMods.Value.Any());
|
||||||
|
}
|
||||||
|
|
||||||
private void createFreeModSelect()
|
private void createFreeModSelect()
|
||||||
{
|
{
|
||||||
AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen
|
AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen
|
||||||
@ -52,5 +85,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
() => freeModSelectScreen.ChildrenOfType<ModColumn>().Any()
|
() => freeModSelectScreen.ChildrenOfType<ModColumn>().Any()
|
||||||
&& freeModSelectScreen.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded));
|
&& freeModSelectScreen.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool assertAllAvailableModsSelected()
|
||||||
|
{
|
||||||
|
var allAvailableMods = availableMods.Value
|
||||||
|
.SelectMany(pair => pair.Value)
|
||||||
|
.Where(mod => mod.UserPlayable && mod.HasImplementation)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var availableMod in allAvailableMods)
|
||||||
|
{
|
||||||
|
if (freeModSelectScreen.SelectedMods.Value.All(selectedMod => selectedMod.GetType() != availableMod.GetType()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -415,6 +415,23 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddAssert("unimplemented mod panel is filtered", () => getPanelForMod(typeof(TestUnimplementedMod)).Filtered.Value);
|
AddAssert("unimplemented mod panel is filtered", () => getPanelForMod(typeof(TestUnimplementedMod)).Filtered.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDeselectAllViaButton()
|
||||||
|
{
|
||||||
|
createScreen();
|
||||||
|
changeRuleset(0);
|
||||||
|
|
||||||
|
AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() });
|
||||||
|
AddAssert("DT + HD selected", () => modSelectScreen.ChildrenOfType<ModPanel>().Count(panel => panel.Active.Value) == 2);
|
||||||
|
|
||||||
|
AddStep("click deselect all button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().Last());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("all mods deselected", () => !SelectedMods.Value.Any());
|
||||||
|
}
|
||||||
|
|
||||||
private void waitForColumnLoad() => AddUntilStep("all column content loaded",
|
private void waitForColumnLoad() => AddUntilStep("all column content loaded",
|
||||||
() => modSelectScreen.ChildrenOfType<ModColumn>().Any() && modSelectScreen.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded));
|
() => modSelectScreen.ChildrenOfType<ModColumn>().Any() && modSelectScreen.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded));
|
||||||
|
|
||||||
|
@ -275,30 +275,25 @@ namespace osu.Game.Overlays.Mods
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
localAvailableMods = newMods;
|
localAvailableMods = newMods;
|
||||||
Scheduler.AddOnce(loadPanels);
|
|
||||||
|
if (!IsLoaded)
|
||||||
|
// if we're coming from BDL, perform the first load synchronously to make sure everything is in place as early as possible.
|
||||||
|
onPanelsLoaded(createPanels());
|
||||||
|
else
|
||||||
|
asyncLoadPanels();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CancellationTokenSource? cancellationTokenSource;
|
private CancellationTokenSource? cancellationTokenSource;
|
||||||
|
|
||||||
private void loadPanels()
|
private void asyncLoadPanels()
|
||||||
{
|
{
|
||||||
cancellationTokenSource?.Cancel();
|
cancellationTokenSource?.Cancel();
|
||||||
|
|
||||||
var panels = localAvailableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)));
|
var panels = createPanels();
|
||||||
|
|
||||||
Task? loadTask;
|
Task? loadTask;
|
||||||
|
|
||||||
latestLoadTask = loadTask = LoadComponentsAsync(panels, loaded =>
|
latestLoadTask = loadTask = LoadComponentsAsync(panels, onPanelsLoaded, (cancellationTokenSource = new CancellationTokenSource()).Token);
|
||||||
{
|
|
||||||
panelFlow.ChildrenEnumerable = loaded;
|
|
||||||
|
|
||||||
updateState();
|
|
||||||
|
|
||||||
foreach (var panel in panelFlow)
|
|
||||||
{
|
|
||||||
panel.Active.BindValueChanged(_ => panelStateChanged(panel));
|
|
||||||
}
|
|
||||||
}, (cancellationTokenSource = new CancellationTokenSource()).Token);
|
|
||||||
loadTask.ContinueWith(_ =>
|
loadTask.ContinueWith(_ =>
|
||||||
{
|
{
|
||||||
if (loadTask == latestLoadTask)
|
if (loadTask == latestLoadTask)
|
||||||
@ -306,6 +301,24 @@ namespace osu.Game.Overlays.Mods
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<ModPanel> createPanels()
|
||||||
|
{
|
||||||
|
var panels = localAvailableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)));
|
||||||
|
return panels;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPanelsLoaded(IEnumerable<ModPanel> loaded)
|
||||||
|
{
|
||||||
|
panelFlow.ChildrenEnumerable = loaded;
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
|
||||||
|
foreach (var panel in panelFlow)
|
||||||
|
{
|
||||||
|
panel.Active.BindValueChanged(_ => panelStateChanged(panel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateState()
|
private void updateState()
|
||||||
{
|
{
|
||||||
foreach (var panel in panelFlow)
|
foreach (var panel in panelFlow)
|
||||||
@ -386,7 +399,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private readonly Queue<Action> pendingSelectionOperations = new Queue<Action>();
|
private readonly Queue<Action> pendingSelectionOperations = new Queue<Action>();
|
||||||
|
|
||||||
protected bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0;
|
internal bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0;
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
|
@ -47,11 +47,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether configurable <see cref="Mod"/>s can be configured by the local user.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual bool AllowCustomisation => true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the total score multiplier calculated from the current selected set of mods should be shown.
|
/// Whether the total score multiplier calculated from the current selected set of mods should be shown.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -59,12 +54,28 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys);
|
protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys);
|
||||||
|
|
||||||
|
protected virtual IEnumerable<ShearedButton> CreateFooterButtons() => new[]
|
||||||
|
{
|
||||||
|
customisationButton = new ShearedToggleButton(200)
|
||||||
|
{
|
||||||
|
Text = "Mod Customisation",
|
||||||
|
Active = { BindTarget = customisationVisible }
|
||||||
|
},
|
||||||
|
new ShearedButton(200)
|
||||||
|
{
|
||||||
|
Text = "Deselect All",
|
||||||
|
Action = DeselectAll
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private readonly BindableBool customisationVisible = new BindableBool();
|
private readonly BindableBool customisationVisible = new BindableBool();
|
||||||
|
|
||||||
private DifficultyMultiplierDisplay? multiplierDisplay;
|
private DifficultyMultiplierDisplay? multiplierDisplay;
|
||||||
private ModSettingsArea modSettingsArea = null!;
|
private ModSettingsArea modSettingsArea = null!;
|
||||||
private ColumnScrollContainer columnScroll = null!;
|
private ColumnScrollContainer columnScroll = null!;
|
||||||
private ColumnFlowContainer columnFlow = null!;
|
private ColumnFlowContainer columnFlow = null!;
|
||||||
|
private ShearedToggleButton? customisationButton;
|
||||||
|
private FillFlowContainer<ShearedButton> footerButtonFlow = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
@ -95,6 +106,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Top = (ShowTotalMultiplier ? DifficultyMultiplierDisplay.HEIGHT : 0) + PADDING,
|
Top = (ShowTotalMultiplier ? DifficultyMultiplierDisplay.HEIGHT : 0) + PADDING,
|
||||||
|
Bottom = PADDING
|
||||||
},
|
},
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
RelativePositionAxes = Axes.Both,
|
RelativePositionAxes = Axes.Both,
|
||||||
@ -145,17 +157,21 @@ namespace osu.Game.Overlays.Mods
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AllowCustomisation)
|
FooterContent.Child = footerButtonFlow = new FillFlowContainer<ShearedButton>
|
||||||
{
|
{
|
||||||
Footer.Add(new ShearedToggleButton(200)
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
Vertical = PADDING,
|
||||||
Origin = Anchor.BottomLeft,
|
Horizontal = 70
|
||||||
Margin = new MarginPadding { Vertical = PADDING, Left = 70 },
|
},
|
||||||
Text = "Mod Customisation",
|
Spacing = new Vector2(10),
|
||||||
Active = { BindTarget = customisationVisible }
|
ChildrenEnumerable = CreateFooterButtons()
|
||||||
});
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null)
|
private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null)
|
||||||
@ -210,7 +226,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private void updateCustomisation(ValueChangedEvent<IReadOnlyList<Mod>> valueChangedEvent)
|
private void updateCustomisation(ValueChangedEvent<IReadOnlyList<Mod>> valueChangedEvent)
|
||||||
{
|
{
|
||||||
if (!AllowCustomisation)
|
if (customisationButton == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool anyCustomisableMod = false;
|
bool anyCustomisableMod = false;
|
||||||
@ -244,6 +260,12 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
MainAreaContent.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic);
|
MainAreaContent.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic);
|
||||||
|
|
||||||
|
foreach (var button in footerButtonFlow)
|
||||||
|
{
|
||||||
|
if (button != customisationButton)
|
||||||
|
button.Enabled.Value = !customisationVisible.Value;
|
||||||
|
}
|
||||||
|
|
||||||
float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0;
|
float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0;
|
||||||
|
|
||||||
modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic);
|
modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic);
|
||||||
@ -319,6 +341,18 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void SelectAll()
|
||||||
|
{
|
||||||
|
foreach (var column in columnFlow.Columns)
|
||||||
|
column.SelectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DeselectAll()
|
||||||
|
{
|
||||||
|
foreach (var column in columnFlow.Columns)
|
||||||
|
column.DeselectAll();
|
||||||
|
}
|
||||||
|
|
||||||
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
{
|
{
|
||||||
if (e.Action == GlobalAction.Back && customisationVisible.Value)
|
if (e.Action == GlobalAction.Back && customisationVisible.Value)
|
||||||
@ -427,6 +461,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
FinishTransforms();
|
FinishTransforms();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate || Column.SelectionAnimationRunning;
|
||||||
|
|
||||||
private void updateDim()
|
private void updateDim()
|
||||||
{
|
{
|
||||||
Colour4 targetColour;
|
Colour4 targetColour;
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
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;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -10,7 +13,6 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
{
|
{
|
||||||
public class FreeModSelectScreen : ModSelectScreen
|
public class FreeModSelectScreen : ModSelectScreen
|
||||||
{
|
{
|
||||||
protected override bool AllowCustomisation => false;
|
|
||||||
protected override bool ShowTotalMultiplier => false;
|
protected override bool ShowTotalMultiplier => false;
|
||||||
|
|
||||||
public new Func<Mod, bool> IsValidMod
|
public new Func<Mod, bool> IsValidMod
|
||||||
@ -25,5 +27,23 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys);
|
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys);
|
||||||
|
|
||||||
|
protected override IEnumerable<ShearedButton> CreateFooterButtons() => new[]
|
||||||
|
{
|
||||||
|
new ShearedButton(200)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Text = "Select All",
|
||||||
|
Action = SelectAll
|
||||||
|
},
|
||||||
|
new ShearedButton(200)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Text = "Deselect All",
|
||||||
|
Action = DeselectAll
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user