mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 02:22:56 +08:00
Merge pull request #19558 from bdach/mod-overlay/create-preset
Add flow for creating new mod presets
This commit is contained in:
commit
55234e8069
@ -1,6 +1,7 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@ -9,19 +10,26 @@ using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public class TestSceneModPresetColumn : OsuTestScene
|
||||
public class TestSceneModPresetColumn : OsuManualInputManagerTestScene
|
||||
{
|
||||
protected override bool UseFreshStoragePerRun => true;
|
||||
|
||||
private Container<Drawable> content = null!;
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
private RulesetStore rulesets = null!;
|
||||
|
||||
[Cached]
|
||||
@ -32,6 +40,12 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||
Dependencies.Cache(Realm);
|
||||
|
||||
base.Content.Add(content = new PopoverContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(30),
|
||||
});
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
@ -57,15 +71,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
public void TestBasicOperation()
|
||||
{
|
||||
AddStep("set osu! ruleset", () => Ruleset.Value = rulesets.GetRuleset(0));
|
||||
AddStep("create content", () => Child = new Container
|
||||
AddStep("create content", () => Child = new ModPresetColumn
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(30),
|
||||
Child = new ModPresetColumn
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
AddUntilStep("3 panels visible", () => this.ChildrenOfType<ModPresetPanel>().Count() == 3);
|
||||
|
||||
@ -112,15 +121,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
public void TestSoftDeleteSupport()
|
||||
{
|
||||
AddStep("set osu! ruleset", () => Ruleset.Value = rulesets.GetRuleset(0));
|
||||
AddStep("create content", () => Child = new Container
|
||||
AddStep("create content", () => Child = new ModPresetColumn
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(30),
|
||||
Child = new ModPresetColumn
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
AddUntilStep("3 panels visible", () => this.ChildrenOfType<ModPresetPanel>().Count() == 3);
|
||||
|
||||
@ -146,6 +150,61 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddUntilStep("3 panels visible", () => this.ChildrenOfType<ModPresetPanel>().Count() == 3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAddingFlow()
|
||||
{
|
||||
ModPresetColumn modPresetColumn = null!;
|
||||
|
||||
AddStep("clear mods", () => SelectedMods.Value = Array.Empty<Mod>());
|
||||
AddStep("create content", () => Child = modPresetColumn = new ModPresetColumn
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
|
||||
AddUntilStep("items loaded", () => modPresetColumn.IsLoaded && modPresetColumn.ItemsLoaded);
|
||||
AddAssert("add preset button disabled", () => !this.ChildrenOfType<AddPresetButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("set mods", () => SelectedMods.Value = new Mod[] { new OsuModDaycore(), new OsuModClassic() });
|
||||
AddAssert("add preset button enabled", () => this.ChildrenOfType<AddPresetButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("click add preset button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<AddPresetButton>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
OsuPopover? popover = null;
|
||||
AddUntilStep("wait for popover", () => (popover = this.ChildrenOfType<OsuPopover>().FirstOrDefault()) != null);
|
||||
AddStep("attempt preset creation", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(popover.ChildrenOfType<ShearedButton>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddWaitStep("wait some", 3);
|
||||
AddAssert("preset creation did not occur", () => this.ChildrenOfType<ModPresetPanel>().Count() == 3);
|
||||
AddUntilStep("popover is unchanged", () => this.ChildrenOfType<OsuPopover>().FirstOrDefault() == popover);
|
||||
|
||||
AddStep("fill preset name", () => popover.ChildrenOfType<LabelledTextBox>().First().Current.Value = "new preset");
|
||||
AddStep("attempt preset creation", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(popover.ChildrenOfType<ShearedButton>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddUntilStep("popover closed", () => !this.ChildrenOfType<OsuPopover>().Any());
|
||||
AddUntilStep("preset creation occurred", () => this.ChildrenOfType<ModPresetPanel>().Count() == 4);
|
||||
|
||||
AddStep("click add preset button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<AddPresetButton>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for popover", () => (popover = this.ChildrenOfType<OsuPopover>().FirstOrDefault()) != null);
|
||||
AddStep("clear mods", () => SelectedMods.Value = Array.Empty<Mod>());
|
||||
AddUntilStep("popover closed", () => !this.ChildrenOfType<OsuPopover>().Any());
|
||||
}
|
||||
|
||||
private ICollection<ModPreset> createTestPresets() => new[]
|
||||
{
|
||||
new ModPreset
|
||||
|
@ -49,15 +49,15 @@ namespace osu.Game.Graphics.UserInterface
|
||||
Active.BindDisabledChanged(disabled => Action = disabled ? null : Active.Toggle, true);
|
||||
Active.BindValueChanged(_ =>
|
||||
{
|
||||
updateActiveState();
|
||||
UpdateActiveState();
|
||||
playSample();
|
||||
});
|
||||
|
||||
updateActiveState();
|
||||
UpdateActiveState();
|
||||
base.LoadComplete();
|
||||
}
|
||||
|
||||
private void updateActiveState()
|
||||
protected virtual void UpdateActiveState()
|
||||
{
|
||||
DarkerColour = Active.Value ? ColourProvider.Highlight1 : ColourProvider.Background3;
|
||||
LighterColour = Active.Value ? ColourProvider.Colour0 : ColourProvider.Background1;
|
||||
|
@ -89,6 +89,16 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"Collections");
|
||||
|
||||
/// <summary>
|
||||
/// "Name"
|
||||
/// </summary>
|
||||
public static LocalisableString Name => new TranslatableString(getKey(@"name"), @"Name");
|
||||
|
||||
/// <summary>
|
||||
/// "Description"
|
||||
/// </summary>
|
||||
public static LocalisableString Description => new TranslatableString(getKey(@"description"), @"Description");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
}
|
@ -29,6 +29,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString PersonalPresets => new TranslatableString(getKey(@"personal_presets"), @"Personal Presets");
|
||||
|
||||
/// <summary>
|
||||
/// "Add preset"
|
||||
/// </summary>
|
||||
public static LocalisableString AddPreset => new TranslatableString(getKey(@"add_preset"), @"Add preset");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
67
osu.Game/Overlays/Mods/AddPresetButton.cs
Normal file
67
osu.Game/Overlays/Mods/AddPresetButton.cs
Normal file
@ -0,0 +1,67 @@
|
||||
// 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.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
public class AddPresetButton : ShearedToggleButton, IHasPopover
|
||||
{
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<IReadOnlyList<Mod>> selectedMods { get; set; } = null!;
|
||||
|
||||
public AddPresetButton()
|
||||
: base(1)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = ModSelectPanel.HEIGHT;
|
||||
|
||||
// shear will be applied at a higher level in `ModPresetColumn`.
|
||||
Content.Shear = Vector2.Zero;
|
||||
Padding = new MarginPadding();
|
||||
|
||||
Text = "+";
|
||||
TextSize = 30;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
selectedMods.BindValueChanged(mods => Enabled.Value = mods.NewValue.Any(), true);
|
||||
Enabled.BindValueChanged(enabled =>
|
||||
{
|
||||
if (!enabled.NewValue)
|
||||
Active.Value = false;
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateActiveState()
|
||||
{
|
||||
DarkerColour = Active.Value ? colours.Orange1 : ColourProvider.Background3;
|
||||
LighterColour = Active.Value ? colours.Orange0 : ColourProvider.Background1;
|
||||
TextColour = Active.Value ? ColourProvider.Background6 : ColourProvider.Content1;
|
||||
|
||||
if (Active.Value)
|
||||
this.ShowPopover();
|
||||
else
|
||||
this.HidePopover();
|
||||
}
|
||||
|
||||
public Popover GetPopover() => new AddPresetPopover(this);
|
||||
}
|
||||
}
|
120
osu.Game/Overlays/Mods/AddPresetPopover.cs
Normal file
120
osu.Game/Overlays/Mods/AddPresetPopover.cs
Normal file
@ -0,0 +1,120 @@
|
||||
// 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.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
internal class AddPresetPopover : OsuPopover
|
||||
{
|
||||
private readonly AddPresetButton button;
|
||||
|
||||
private readonly LabelledTextBox nameTextBox;
|
||||
private readonly LabelledTextBox descriptionTextBox;
|
||||
private readonly ShearedButton createButton;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<RulesetInfo> ruleset { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<IReadOnlyList<Mod>> selectedMods { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; } = null!;
|
||||
|
||||
public AddPresetPopover(AddPresetButton addPresetButton)
|
||||
{
|
||||
button = addPresetButton;
|
||||
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
Width = 300,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(7),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
nameTextBox = new LabelledTextBox
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Label = CommonStrings.Name,
|
||||
TabbableContentContainer = this
|
||||
},
|
||||
descriptionTextBox = new LabelledTextBox
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Label = CommonStrings.Description,
|
||||
TabbableContentContainer = this
|
||||
},
|
||||
createButton = new ShearedButton
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = ModSelectOverlayStrings.AddPreset,
|
||||
Action = tryCreatePreset
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider, OsuColour colours)
|
||||
{
|
||||
Body.BorderThickness = 3;
|
||||
Body.BorderColour = colours.Orange1;
|
||||
|
||||
createButton.DarkerColour = colours.Orange1;
|
||||
createButton.LighterColour = colours.Orange0;
|
||||
createButton.TextColour = colourProvider.Background6;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(nameTextBox));
|
||||
}
|
||||
|
||||
private void tryCreatePreset()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(nameTextBox.Current.Value))
|
||||
{
|
||||
Body.Shake();
|
||||
return;
|
||||
}
|
||||
|
||||
realm.Write(r => r.Add(new ModPreset
|
||||
{
|
||||
Name = nameTextBox.Current.Value,
|
||||
Description = descriptionTextBox.Current.Value,
|
||||
Mods = selectedMods.Value.ToArray(),
|
||||
Ruleset = r.Find<RulesetInfo>(ruleset.Value.ShortName)
|
||||
}));
|
||||
|
||||
this.HidePopover();
|
||||
}
|
||||
|
||||
protected override void UpdateState(ValueChangedEvent<Visibility> state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
if (state.NewValue == Visibility.Hidden)
|
||||
button.Active.Value = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,10 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
AccentColour = colours.Orange1;
|
||||
HeaderText = ModSelectOverlayStrings.PersonalPresets;
|
||||
|
||||
AddPresetButton addPresetButton;
|
||||
ItemsFlow.Add(addPresetButton = new AddPresetButton());
|
||||
ItemsFlow.SetLayoutPosition(addPresetButton, float.PositiveInfinity);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -64,7 +68,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
if (!presets.Any())
|
||||
{
|
||||
ItemsFlow.Clear();
|
||||
ItemsFlow.RemoveAll(panel => panel is ModPresetPanel);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -77,7 +81,8 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
latestLoadTask = loadTask = LoadComponentsAsync(panels, loaded =>
|
||||
{
|
||||
ItemsFlow.ChildrenEnumerable = loaded;
|
||||
ItemsFlow.RemoveAll(panel => panel is ModPresetPanel);
|
||||
ItemsFlow.AddRange(loaded);
|
||||
}, (cancellationTokenSource = new CancellationTokenSource()).Token);
|
||||
loadTask.ContinueWith(_ =>
|
||||
{
|
||||
|
@ -43,8 +43,7 @@ namespace osu.Game.Overlays.Mods
|
||||
}
|
||||
|
||||
public const float CORNER_RADIUS = 7;
|
||||
|
||||
protected const float HEIGHT = 42;
|
||||
public const float HEIGHT = 42;
|
||||
|
||||
protected virtual float IdleSwitchWidth => 14;
|
||||
protected virtual float ExpandedSwitchWidth => 30;
|
||||
|
Loading…
Reference in New Issue
Block a user