1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-08 05:50:05 +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:
Dean Herbert 2022-05-07 17:32:10 +09:00 committed by GitHub
commit 2752bdf04f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 166 additions and 31 deletions

View File

@ -1,19 +1,32 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.OnlinePlay;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneFreeModSelectScreen : MultiplayerTestScene
{
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]
public void TestFreeModSelect()
@ -42,6 +55,26 @@ namespace osu.Game.Tests.Visual.Multiplayer
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()
{
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>().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;
}
}
}

View File

@ -415,6 +415,23 @@ namespace osu.Game.Tests.Visual.UserInterface
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",
() => modSelectScreen.ChildrenOfType<ModColumn>().Any() && modSelectScreen.ChildrenOfType<ModColumn>().All(column => column.IsLoaded && column.ItemsLoaded));

View File

@ -275,30 +275,25 @@ namespace osu.Game.Overlays.Mods
return;
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 void loadPanels()
private void asyncLoadPanels()
{
cancellationTokenSource?.Cancel();
var panels = localAvailableMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)));
var panels = createPanels();
Task? loadTask;
latestLoadTask = loadTask = LoadComponentsAsync(panels, loaded =>
{
panelFlow.ChildrenEnumerable = loaded;
updateState();
foreach (var panel in panelFlow)
{
panel.Active.BindValueChanged(_ => panelStateChanged(panel));
}
}, (cancellationTokenSource = new CancellationTokenSource()).Token);
latestLoadTask = loadTask = LoadComponentsAsync(panels, onPanelsLoaded, (cancellationTokenSource = new CancellationTokenSource()).Token);
loadTask.ContinueWith(_ =>
{
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()
{
foreach (var panel in panelFlow)
@ -386,7 +399,7 @@ namespace osu.Game.Overlays.Mods
private readonly Queue<Action> pendingSelectionOperations = new Queue<Action>();
protected bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0;
internal bool SelectionAnimationRunning => pendingSelectionOperations.Count > 0;
protected override void Update()
{

View File

@ -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>
/// Whether the total score multiplier calculated from the current selected set of mods should be shown.
/// </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 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 DifficultyMultiplierDisplay? multiplierDisplay;
private ModSettingsArea modSettingsArea = null!;
private ColumnScrollContainer columnScroll = null!;
private ColumnFlowContainer columnFlow = null!;
private ShearedToggleButton? customisationButton;
private FillFlowContainer<ShearedButton> footerButtonFlow = null!;
[BackgroundDependencyLoader]
private void load()
@ -95,6 +106,7 @@ namespace osu.Game.Overlays.Mods
Padding = new MarginPadding
{
Top = (ShowTotalMultiplier ? DifficultyMultiplierDisplay.HEIGHT : 0) + PADDING,
Bottom = PADDING
},
RelativeSizeAxes = 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,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Vertical = PADDING, Left = 70 },
Text = "Mod Customisation",
Active = { BindTarget = customisationVisible }
});
}
Vertical = PADDING,
Horizontal = 70
},
Spacing = new Vector2(10),
ChildrenEnumerable = CreateFooterButtons()
};
}
private ColumnDimContainer createModColumnContent(ModType modType, Key[]? toggleKeys = null)
@ -210,7 +226,7 @@ namespace osu.Game.Overlays.Mods
private void updateCustomisation(ValueChangedEvent<IReadOnlyList<Mod>> valueChangedEvent)
{
if (!AllowCustomisation)
if (customisationButton == null)
return;
bool anyCustomisableMod = false;
@ -244,6 +260,12 @@ namespace osu.Game.Overlays.Mods
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;
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)
{
if (e.Action == GlobalAction.Back && customisationVisible.Value)
@ -427,6 +461,8 @@ namespace osu.Game.Overlays.Mods
FinishTransforms();
}
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate || Column.SelectionAnimationRunning;
private void updateDim()
{
Colour4 targetColour;

View File

@ -2,6 +2,9 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets.Mods;
using osuTK.Input;
@ -10,7 +13,6 @@ namespace osu.Game.Screens.OnlinePlay
{
public class FreeModSelectScreen : ModSelectScreen
{
protected override bool AllowCustomisation => false;
protected override bool ShowTotalMultiplier => false;
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 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
}
};
}
}