mirror of
https://github.com/ppy/osu.git
synced 2024-11-12 04:49:40 +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.
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user