mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 01:52:55 +08:00
Merge pull request #18419 from bdach/mod-overlay/enable-disable-select-deselect
Disable select/deselect all buttons on mod overlay when they have no effect
This commit is contained in:
commit
c524b665ad
@ -9,7 +9,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
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;
|
||||
@ -73,19 +72,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createFreeModSelect();
|
||||
|
||||
AddAssert("select all button enabled", () => this.ChildrenOfType<SelectAllModsButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("click select all button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().ElementAt(1));
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<SelectAllModsButton>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddUntilStep("all mods selected", assertAllAvailableModsSelected);
|
||||
AddAssert("select all button disabled", () => !this.ChildrenOfType<SelectAllModsButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("click deselect all button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().Last());
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<DeselectAllModsButton>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddUntilStep("all mods deselected", () => !freeModSelectOverlay.SelectedMods.Value.Any());
|
||||
AddAssert("select all button enabled", () => this.ChildrenOfType<SelectAllModsButton>().Single().Enabled.Value);
|
||||
}
|
||||
|
||||
private void createFreeModSelect()
|
||||
|
@ -435,15 +435,19 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
createScreen();
|
||||
changeRuleset(0);
|
||||
|
||||
AddAssert("deselect all button disabled", () => !this.ChildrenOfType<DeselectAllModsButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("select DT + HD", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime(), new OsuModHidden() });
|
||||
AddAssert("DT + HD selected", () => modSelectOverlay.ChildrenOfType<ModPanel>().Count(panel => panel.Active.Value) == 2);
|
||||
AddAssert("deselect all button enabled", () => this.ChildrenOfType<DeselectAllModsButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("click deselect all button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<ShearedButton>().Last());
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<DeselectAllModsButton>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddUntilStep("all mods deselected", () => !SelectedMods.Value.Any());
|
||||
AddAssert("deselect all button disabled", () => !this.ChildrenOfType<DeselectAllModsButton>().Single().Enabled.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
54
osu.Game/Overlays/Mods/DeselectAllModsButton.cs
Normal file
54
osu.Game/Overlays/Mods/DeselectAllModsButton.cs
Normal file
@ -0,0 +1,54 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
public class DeselectAllModsButton : ShearedButton, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
private readonly Bindable<IReadOnlyList<Mod>> selectedMods = new Bindable<IReadOnlyList<Mod>>();
|
||||
|
||||
public DeselectAllModsButton(ModSelectOverlay modSelectOverlay)
|
||||
: base(ModSelectOverlay.BUTTON_WIDTH)
|
||||
{
|
||||
Text = CommonStrings.DeselectAll;
|
||||
Action = modSelectOverlay.DeselectAll;
|
||||
|
||||
selectedMods.BindTo(modSelectOverlay.SelectedMods);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
selectedMods.BindValueChanged(_ => updateEnabledState(), true);
|
||||
}
|
||||
|
||||
private void updateEnabledState()
|
||||
{
|
||||
Enabled.Value = selectedMods.Value.Any();
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat || e.Action != GlobalAction.DeselectAllMods)
|
||||
return false;
|
||||
|
||||
TriggerClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -29,11 +29,20 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
public abstract class ModSelectOverlay : ShearedOverlayContainer, ISamplePlaybackDisabler
|
||||
{
|
||||
protected const int BUTTON_WIDTH = 200;
|
||||
public const int BUTTON_WIDTH = 200;
|
||||
|
||||
[Cached]
|
||||
public Bindable<IReadOnlyList<Mod>> SelectedMods { get; private set; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
|
||||
/// <summary>
|
||||
/// Contains a dictionary with the current <see cref="ModState"/> of all mods applicable for the current ruleset.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Contrary to <see cref="OsuGameBase.AvailableMods"/> and <see cref="globalAvailableMods"/>, the <see cref="Mod"/> instances
|
||||
/// inside the <see cref="ModState"/> objects are owned solely by this <see cref="ModSelectOverlay"/> instance.
|
||||
/// </remarks>
|
||||
public Bindable<Dictionary<ModType, IReadOnlyList<ModState>>> AvailableMods { get; } = new Bindable<Dictionary<ModType, IReadOnlyList<ModState>>>(new Dictionary<ModType, IReadOnlyList<ModState>>());
|
||||
|
||||
private Func<Mod, bool> isValidMod = m => true;
|
||||
|
||||
/// <summary>
|
||||
@ -76,16 +85,12 @@ namespace osu.Game.Overlays.Mods
|
||||
};
|
||||
}
|
||||
|
||||
yield return deselectAllButton = new ShearedButton(BUTTON_WIDTH)
|
||||
{
|
||||
Text = CommonStrings.DeselectAll,
|
||||
Action = DeselectAll
|
||||
};
|
||||
yield return new DeselectAllModsButton(this);
|
||||
}
|
||||
|
||||
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> availableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
||||
private readonly Dictionary<ModType, IReadOnlyList<ModState>> localAvailableMods = new Dictionary<ModType, IReadOnlyList<ModState>>();
|
||||
private IEnumerable<ModState> allLocalAvailableMods => localAvailableMods.SelectMany(pair => pair.Value);
|
||||
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> globalAvailableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
||||
|
||||
private IEnumerable<ModState> allAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value);
|
||||
|
||||
private readonly BindableBool customisationVisible = new BindableBool();
|
||||
|
||||
@ -98,7 +103,6 @@ namespace osu.Game.Overlays.Mods
|
||||
private DifficultyMultiplierDisplay? multiplierDisplay;
|
||||
|
||||
private ShearedToggleButton? customisationButton;
|
||||
private ShearedButton? deselectAllButton;
|
||||
|
||||
protected ModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green)
|
||||
: base(colourScheme)
|
||||
@ -209,13 +213,13 @@ namespace osu.Game.Overlays.Mods
|
||||
})
|
||||
};
|
||||
|
||||
availableMods.BindTo(game.AvailableMods);
|
||||
globalAvailableMods.BindTo(game.AvailableMods);
|
||||
}
|
||||
|
||||
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.
|
||||
availableMods.BindValueChanged(_ => createLocalMods(), true);
|
||||
globalAvailableMods.BindValueChanged(_ => createLocalMods(), true);
|
||||
|
||||
base.LoadComplete();
|
||||
|
||||
@ -247,7 +251,7 @@ namespace osu.Game.Overlays.Mods
|
||||
/// <summary>
|
||||
/// Select all visible mods in all columns.
|
||||
/// </summary>
|
||||
protected void SelectAll()
|
||||
public void SelectAll()
|
||||
{
|
||||
foreach (var column in columnFlow.Columns)
|
||||
column.SelectAll();
|
||||
@ -256,7 +260,7 @@ namespace osu.Game.Overlays.Mods
|
||||
/// <summary>
|
||||
/// Deselect all visible mods in all columns.
|
||||
/// </summary>
|
||||
protected void DeselectAll()
|
||||
public void DeselectAll()
|
||||
{
|
||||
foreach (var column in columnFlow.Columns)
|
||||
column.DeselectAll();
|
||||
@ -280,9 +284,9 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
private void createLocalMods()
|
||||
{
|
||||
localAvailableMods.Clear();
|
||||
var newLocalAvailableMods = new Dictionary<ModType, IReadOnlyList<ModState>>();
|
||||
|
||||
foreach (var (modType, mods) in availableMods.Value)
|
||||
foreach (var (modType, mods) in globalAvailableMods.Value)
|
||||
{
|
||||
var modStates = mods.SelectMany(ModUtils.FlattenMod)
|
||||
.Select(mod => new ModState(mod.DeepClone()))
|
||||
@ -291,18 +295,19 @@ namespace osu.Game.Overlays.Mods
|
||||
foreach (var modState in modStates)
|
||||
modState.Active.BindValueChanged(_ => updateFromInternalSelection());
|
||||
|
||||
localAvailableMods[modType] = modStates;
|
||||
newLocalAvailableMods[modType] = modStates;
|
||||
}
|
||||
|
||||
AvailableMods.Value = newLocalAvailableMods;
|
||||
filterMods();
|
||||
|
||||
foreach (var column in columnFlow.Columns)
|
||||
column.AvailableMods = localAvailableMods.GetValueOrDefault(column.ModType, Array.Empty<ModState>());
|
||||
column.AvailableMods = AvailableMods.Value.GetValueOrDefault(column.ModType, Array.Empty<ModState>());
|
||||
}
|
||||
|
||||
private void filterMods()
|
||||
{
|
||||
foreach (var modState in allLocalAvailableMods)
|
||||
foreach (var modState in allAvailableMods)
|
||||
modState.Filtered.Value = !modState.Mod.HasImplementation || !IsValidMod.Invoke(modState.Mod);
|
||||
}
|
||||
|
||||
@ -383,7 +388,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
var newSelection = new List<Mod>();
|
||||
|
||||
foreach (var modState in allLocalAvailableMods)
|
||||
foreach (var modState in allAvailableMods)
|
||||
{
|
||||
var matchingSelectedMod = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == modState.Mod.GetType());
|
||||
|
||||
@ -410,7 +415,7 @@ namespace osu.Game.Overlays.Mods
|
||||
if (externalSelectionUpdateInProgress)
|
||||
return;
|
||||
|
||||
var candidateSelection = allLocalAvailableMods.Where(modState => modState.Active.Value)
|
||||
var candidateSelection = allAvailableMods.Where(modState => modState.Active.Value)
|
||||
.Select(modState => modState.Mod)
|
||||
.ToArray();
|
||||
|
||||
@ -514,10 +519,6 @@ namespace osu.Game.Overlays.Mods
|
||||
hideOverlay(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
case GlobalAction.DeselectAllMods:
|
||||
deselectAllButton?.TriggerClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnPressed(e);
|
||||
|
61
osu.Game/Overlays/Mods/SelectAllModsButton.cs
Normal file
61
osu.Game/Overlays/Mods/SelectAllModsButton.cs
Normal file
@ -0,0 +1,61 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
public class SelectAllModsButton : ShearedButton, IKeyBindingHandler<PlatformAction>
|
||||
{
|
||||
private readonly Bindable<IReadOnlyList<Mod>> selectedMods = new Bindable<IReadOnlyList<Mod>>();
|
||||
private readonly Bindable<Dictionary<ModType, IReadOnlyList<ModState>>> availableMods = new Bindable<Dictionary<ModType, IReadOnlyList<ModState>>>();
|
||||
|
||||
public SelectAllModsButton(FreeModSelectOverlay modSelectOverlay)
|
||||
: base(ModSelectOverlay.BUTTON_WIDTH)
|
||||
{
|
||||
Text = CommonStrings.SelectAll;
|
||||
Action = modSelectOverlay.SelectAll;
|
||||
|
||||
selectedMods.BindTo(modSelectOverlay.SelectedMods);
|
||||
availableMods.BindTo(modSelectOverlay.AvailableMods);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
selectedMods.BindValueChanged(_ => Scheduler.AddOnce(updateEnabledState));
|
||||
availableMods.BindValueChanged(_ => Scheduler.AddOnce(updateEnabledState));
|
||||
updateEnabledState();
|
||||
}
|
||||
|
||||
private void updateEnabledState()
|
||||
{
|
||||
Enabled.Value = availableMods.Value
|
||||
.SelectMany(pair => pair.Value)
|
||||
.Any(modState => !modState.Active.Value && !modState.Filtered.Value);
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
||||
{
|
||||
if (e.Repeat || e.Action != PlatformAction.SelectAll)
|
||||
return false;
|
||||
|
||||
TriggerClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<PlatformAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -6,18 +6,14 @@ using osu.Game.Overlays;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osuTK.Input;
|
||||
using osu.Game.Localisation;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
public class FreeModSelectOverlay : ModSelectOverlay, IKeyBindingHandler<PlatformAction>
|
||||
public class FreeModSelectOverlay : ModSelectOverlay
|
||||
{
|
||||
protected override bool ShowTotalMultiplier => false;
|
||||
|
||||
@ -29,8 +25,6 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
set => base.IsValidMod = m => m.UserPlayable && value.Invoke(m);
|
||||
}
|
||||
|
||||
private ShearedButton selectAllButton;
|
||||
|
||||
public FreeModSelectOverlay()
|
||||
: base(OverlayColourScheme.Plum)
|
||||
{
|
||||
@ -40,31 +34,10 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys);
|
||||
|
||||
protected override IEnumerable<ShearedButton> CreateFooterButtons() => base.CreateFooterButtons().Prepend(
|
||||
selectAllButton = new ShearedButton(BUTTON_WIDTH)
|
||||
new SelectAllModsButton(this)
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Text = CommonStrings.SelectAll,
|
||||
Action = SelectAll
|
||||
});
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case PlatformAction.SelectAll:
|
||||
selectAllButton.TriggerClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<PlatformAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user