diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs new file mode 100644 index 0000000000..00f2979691 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneDesignSection.cs @@ -0,0 +1,100 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Globalization; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Setup; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Editing +{ + public class TestSceneDesignSection : OsuManualInputManagerTestScene + { + private TestDesignSection designSection; + private EditorBeatmap editorBeatmap { get; set; } + + [SetUpSteps] + public void SetUp() + { + AddStep("create blank beatmap", () => editorBeatmap = new EditorBeatmap(new Beatmap())); + AddStep("create section", () => Child = new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] + { + (typeof(EditorBeatmap), editorBeatmap) + }, + Child = designSection = new TestDesignSection() + }); + } + + [Test] + public void TestCountdownOff() + { + AddStep("turn countdown off", () => designSection.EnableCountdown.Current.Value = false); + + AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.None); + AddUntilStep("other controls hidden", () => !designSection.CountdownSettings.IsPresent); + } + + [Test] + public void TestCountdownOn() + { + AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true); + + AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal); + AddUntilStep("other controls shown", () => designSection.CountdownSettings.IsPresent); + + AddStep("change countdown speed", () => designSection.CountdownSpeed.Current.Value = CountdownType.DoubleSpeed); + + AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.DoubleSpeed); + AddUntilStep("other controls still shown", () => designSection.CountdownSettings.IsPresent); + } + + [Test] + public void TestCountdownOffset() + { + AddStep("turn countdown on", () => designSection.EnableCountdown.Current.Value = true); + + AddAssert("beatmap has correct type", () => editorBeatmap.BeatmapInfo.Countdown == CountdownType.Normal); + + checkOffsetAfter("1", 1); + checkOffsetAfter(string.Empty, 0); + checkOffsetAfter("123", 123); + checkOffsetAfter("0", 0); + } + + private void checkOffsetAfter(string userInput, int expectedFinalValue) + { + AddStep("click text box", () => + { + var textBox = designSection.CountdownOffset.ChildrenOfType().Single(); + InputManager.MoveMouseTo(textBox); + InputManager.Click(MouseButton.Left); + }); + AddStep("set offset text", () => designSection.CountdownOffset.Current.Value = userInput); + AddStep("commit text", () => InputManager.Key(Key.Enter)); + + AddAssert($"displayed value is {expectedFinalValue}", () => designSection.CountdownOffset.Current.Value == expectedFinalValue.ToString(CultureInfo.InvariantCulture)); + AddAssert($"beatmap value is {expectedFinalValue}", () => editorBeatmap.BeatmapInfo.CountdownOffset == expectedFinalValue); + } + + private class TestDesignSection : DesignSection + { + public new LabelledSwitchButton EnableCountdown => base.EnableCountdown; + + public new FillFlowContainer CountdownSettings => base.CountdownSettings; + public new LabelledEnumDropdown CountdownSpeed => base.CountdownSpeed; + public new LabelledNumberBox CountdownOffset => base.CountdownOffset; + } + } +} diff --git a/osu.Game/Beatmaps/CountdownType.cs b/osu.Game/Beatmaps/CountdownType.cs index 1831b4576b..73f85bf701 100644 --- a/osu.Game/Beatmaps/CountdownType.cs +++ b/osu.Game/Beatmaps/CountdownType.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.ComponentModel; + namespace osu.Game.Beatmaps { /// @@ -9,8 +11,14 @@ namespace osu.Game.Beatmaps public enum CountdownType { None = 0, + + [Description("Normal")] Normal = 1, + + [Description("Half speed")] HalfSpeed = 2, + + [Description("Double speed")] DoubleSpeed = 3 } } diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledNumberBox.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledNumberBox.cs new file mode 100644 index 0000000000..ca247ab679 --- /dev/null +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledNumberBox.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Graphics.UserInterfaceV2 +{ + public class LabelledNumberBox : LabelledTextBox + { + protected override OsuTextBox CreateTextBox() => new OsuNumberBox(); + } +} diff --git a/osu.Game/Screens/Edit/Setup/DesignSection.cs b/osu.Game/Screens/Edit/Setup/DesignSection.cs index 68aaf3dd76..90f95a668e 100644 --- a/osu.Game/Screens/Edit/Setup/DesignSection.cs +++ b/osu.Game/Screens/Edit/Setup/DesignSection.cs @@ -1,14 +1,27 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Globalization; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; +using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterfaceV2; +using osuTK; namespace osu.Game.Screens.Edit.Setup { internal class DesignSection : SetupSection { + protected LabelledSwitchButton EnableCountdown; + + protected FillFlowContainer CountdownSettings; + protected LabelledEnumDropdown CountdownSpeed; + protected LabelledNumberBox CountdownOffset; + private LabelledSwitchButton widescreenSupport; private LabelledSwitchButton epilepsyWarning; private LabelledSwitchButton letterboxDuringBreaks; @@ -20,6 +33,35 @@ namespace osu.Game.Screens.Edit.Setup { Children = new[] { + EnableCountdown = new LabelledSwitchButton + { + Label = "Enable countdown", + Current = { Value = Beatmap.BeatmapInfo.Countdown != CountdownType.None }, + Description = "If enabled, an \"Are you ready? 3, 2, 1, GO!\" countdown will be inserted at the beginning of the beatmap, assuming there is enough time to do so." + }, + CountdownSettings = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10), + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + CountdownSpeed = new LabelledEnumDropdown + { + Label = "Countdown speed", + Current = { Value = Beatmap.BeatmapInfo.Countdown != CountdownType.None ? Beatmap.BeatmapInfo.Countdown : CountdownType.Normal }, + Items = Enum.GetValues(typeof(CountdownType)).Cast().Where(type => type != CountdownType.None) + }, + CountdownOffset = new LabelledNumberBox + { + Label = "Countdown offset", + Current = { Value = Beatmap.BeatmapInfo.CountdownOffset.ToString() }, + Description = "If the countdown sounds off-time, use this to make it appear one or more beats early.", + } + } + }, + Empty(), widescreenSupport = new LabelledSwitchButton { Label = "Widescreen support", @@ -45,13 +87,31 @@ namespace osu.Game.Screens.Edit.Setup { base.LoadComplete(); + EnableCountdown.Current.BindValueChanged(_ => updateCountdownSettingsVisibility(), true); + + EnableCountdown.Current.BindValueChanged(_ => updateBeatmap()); + CountdownSpeed.Current.BindValueChanged(_ => updateBeatmap()); + CountdownOffset.OnCommit += (_, __) => onOffsetCommitted(); + widescreenSupport.Current.BindValueChanged(_ => updateBeatmap()); epilepsyWarning.Current.BindValueChanged(_ => updateBeatmap()); letterboxDuringBreaks.Current.BindValueChanged(_ => updateBeatmap()); } + private void updateCountdownSettingsVisibility() => CountdownSettings.FadeTo(EnableCountdown.Current.Value ? 1 : 0); + + private void onOffsetCommitted() + { + updateBeatmap(); + // update displayed text to ensure parsed value matches display (i.e. if empty string was provided). + CountdownOffset.Current.Value = Beatmap.BeatmapInfo.CountdownOffset.ToString(CultureInfo.InvariantCulture); + } + private void updateBeatmap() { + Beatmap.BeatmapInfo.Countdown = EnableCountdown.Current.Value ? CountdownSpeed.Current.Value : CountdownType.None; + Beatmap.BeatmapInfo.CountdownOffset = int.TryParse(CountdownOffset.Current.Value, NumberStyles.None, CultureInfo.InvariantCulture, out int offset) ? offset : 0; + Beatmap.BeatmapInfo.WidescreenStoryboard = widescreenSupport.Current.Value; Beatmap.BeatmapInfo.EpilepsyWarning = epilepsyWarning.Current.Value; Beatmap.BeatmapInfo.LetterboxInBreaks = letterboxDuringBreaks.Current.Value;