From f3a5258c5bdea596fe114418f3c36a9c625f4624 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 14:05:12 +0900 Subject: [PATCH 01/12] Reorder file (ctor + bdl) --- osu.Game/Screens/Play/MenuOverlay.cs | 132 +++++++++++++-------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/osu.Game/Screens/Play/MenuOverlay.cs b/osu.Game/Screens/Play/MenuOverlay.cs index 0a8e172e57..6d25c33243 100644 --- a/osu.Game/Screens/Play/MenuOverlay.cs +++ b/osu.Game/Screens/Play/MenuOverlay.cs @@ -32,73 +32,11 @@ namespace osu.Game.Screens.Play protected FillFlowContainer Buttons; - public int Retries - { - set - { - if (retryCounterContainer != null) - { - // "You've retried 1,065 times in this session" - // "You've retried 1 time in this session" - - retryCounterContainer.Children = new Drawable[] - { - new OsuSpriteText - { - Text = "You've retried ", - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f), - TextSize = 18 - }, - new OsuSpriteText - { - Text = $"{value:n0}", - Font = @"Exo2.0-Bold", - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f), - TextSize = 18 - }, - new OsuSpriteText - { - Text = $" time{(value == 1 ? "" : "s")} in this session", - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f), - TextSize = 18 - } - }; - } - } - } - private FillFlowContainer retryCounterContainer; - public override bool HandleInput => State == Visibility.Visible; - - protected override void PopIn() => this.FadeIn(transition_duration, Easing.In); - protected override void PopOut() => this.FadeOut(transition_duration, Easing.In); - - // Don't let mouse down events through the overlay or people can click circles while paused. - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => true; - - protected override bool OnMouseMove(InputState state) => true; - - protected void AddButton(string text, Color4 colour, Action action) + protected MenuOverlay() { - Buttons.Add(new Button - { - Text = text, - ButtonColour = colour, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Height = button_height, - Action = delegate - { - action?.Invoke(); - Hide(); - } - }); + RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] @@ -182,9 +120,71 @@ namespace osu.Game.Screens.Play Retries = 0; } - protected MenuOverlay() + public int Retries { - RelativeSizeAxes = Axes.Both; + set + { + if (retryCounterContainer != null) + { + // "You've retried 1,065 times in this session" + // "You've retried 1 time in this session" + + retryCounterContainer.Children = new Drawable[] + { + new OsuSpriteText + { + Text = "You've retried ", + Shadow = true, + ShadowColour = new Color4(0, 0, 0, 0.25f), + TextSize = 18 + }, + new OsuSpriteText + { + Text = $"{value:n0}", + Font = @"Exo2.0-Bold", + Shadow = true, + ShadowColour = new Color4(0, 0, 0, 0.25f), + TextSize = 18 + }, + new OsuSpriteText + { + Text = $" time{(value == 1 ? "" : "s")} in this session", + Shadow = true, + ShadowColour = new Color4(0, 0, 0, 0.25f), + TextSize = 18 + } + }; + } + } + } + + public override bool HandleInput => State == Visibility.Visible; + + protected override void PopIn() => this.FadeIn(transition_duration, Easing.In); + protected override void PopOut() => this.FadeOut(transition_duration, Easing.In); + + // Don't let mouse down events through the overlay or people can click circles while paused. + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => true; + + protected override bool OnMouseMove(InputState state) => true; + + protected void AddButton(string text, Color4 colour, Action action) + { + Buttons.Add(new Button + { + Text = text, + ButtonColour = colour, + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Height = button_height, + Action = delegate + { + action?.Invoke(); + Hide(); + } + }); } public class Button : DialogButton From 8fdaf6f8f41a6520abc1ffecfb423f5b69aab8d0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 14:47:17 +0900 Subject: [PATCH 02/12] Restructure DialogButton to support selection --- .../Graphics/UserInterface/DialogButton.cs | 251 +++++++++--------- 1 file changed, 132 insertions(+), 119 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index bb62815a7b..3b3cb953de 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -12,6 +12,7 @@ using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; using osu.Framework.Extensions.Color4Extensions; using osu.Game.Graphics.Containers; +using osu.Framework.Configuration; namespace osu.Game.Graphics.UserInterface { @@ -22,62 +23,7 @@ namespace osu.Game.Graphics.UserInterface private const float glow_fade_duration = 250; private const float click_duration = 200; - private Color4 buttonColour; - public Color4 ButtonColour - { - get - { - return buttonColour; - } - set - { - buttonColour = value; - updateGlow(); - colourContainer.Colour = value; - } - } - - private Color4 backgroundColour = OsuColour.Gray(34); - public Color4 BackgroundColour - { - get - { - return backgroundColour; - } - set - { - backgroundColour = value; - background.Colour = value; - } - } - - private string text; - public string Text - { - get - { - return text; - } - set - { - text = value; - spriteText.Text = Text; - } - } - - private float textSize = 28; - public float TextSize - { - get - { - return textSize; - } - set - { - textSize = value; - spriteText.TextSize = value; - } - } + public readonly BindableBool Selected = new BindableBool(); private readonly Container backgroundContainer; private readonly Container colourContainer; @@ -91,69 +37,6 @@ namespace osu.Game.Graphics.UserInterface private bool didClick; // Used for making sure that the OnMouseDown animation can call instead of OnHoverLost's when clicking - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceiveMouseInputAt(screenSpacePos); - - protected override bool OnClick(Framework.Input.InputState state) - { - didClick = true; - colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In); - flash(); - - this.Delay(click_duration).Schedule(delegate - { - colourContainer.ResizeTo(new Vector2(0.8f, 1f)); - spriteText.Spacing = Vector2.Zero; - glowContainer.FadeOut(); - }); - - return base.OnClick(state); - } - - protected override bool OnHover(Framework.Input.InputState state) - { - spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic); - - colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, Easing.OutElastic); - glowContainer.FadeIn(glow_fade_duration, Easing.Out); - base.OnHover(state); - return true; - } - - protected override void OnHoverLost(Framework.Input.InputState state) - { - if (!didClick) - { - colourContainer.ResizeTo(new Vector2(0.8f, 1f), hover_duration, Easing.OutElastic); - spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic); - glowContainer.FadeOut(glow_fade_duration, Easing.Out); - } - - didClick = false; - } - - private void flash() - { - var flash = new Box - { - RelativeSizeAxes = Axes.Both - }; - - colourContainer.Add(flash); - - flash.Colour = ButtonColour; - flash.Blending = BlendingMode.Additive; - flash.Alpha = 0.3f; - flash.FadeOutFromOne(click_duration); - flash.Expire(); - } - - private void updateGlow() - { - leftGlow.Colour = ColourInfo.GradientHorizontal(new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f), ButtonColour); - centerGlow.Colour = ButtonColour; - rightGlow.Colour = ColourInfo.GradientHorizontal(ButtonColour, new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f)); - } - public DialogButton() { RelativeSizeAxes = Axes.X; @@ -268,6 +151,136 @@ namespace osu.Game.Graphics.UserInterface }; updateGlow(); + + Selected.ValueChanged += selectionChanged; + } + + private Color4 buttonColour; + public Color4 ButtonColour + { + get + { + return buttonColour; + } + set + { + buttonColour = value; + updateGlow(); + colourContainer.Colour = value; + } + } + + private Color4 backgroundColour = OsuColour.Gray(34); + public Color4 BackgroundColour + { + get + { + return backgroundColour; + } + set + { + backgroundColour = value; + background.Colour = value; + } + } + + private string text; + public string Text + { + get + { + return text; + } + set + { + text = value; + spriteText.Text = Text; + } + } + + private float textSize = 28; + public float TextSize + { + get + { + return textSize; + } + set + { + textSize = value; + spriteText.TextSize = value; + } + } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceiveMouseInputAt(screenSpacePos); + + protected override bool OnClick(Framework.Input.InputState state) + { + didClick = true; + colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In); + flash(); + + this.Delay(click_duration).Schedule(delegate + { + colourContainer.ResizeTo(new Vector2(0.8f, 1f)); + spriteText.Spacing = Vector2.Zero; + glowContainer.FadeOut(); + }); + + return base.OnClick(state); + } + + protected override bool OnHover(Framework.Input.InputState state) + { + base.OnHover(state); + + Selected.Value = true; + return true; + } + + protected override void OnHoverLost(Framework.Input.InputState state) + { + base.OnHoverLost(state); + Selected.Value = false; + } + + private void selectionChanged(bool isSelected) + { + if (isSelected) + { + spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic); + colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, Easing.OutElastic); + glowContainer.FadeIn(glow_fade_duration, Easing.Out); + } + else if (!didClick) + { + colourContainer.ResizeTo(new Vector2(0.8f, 1f), hover_duration, Easing.OutElastic); + spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic); + glowContainer.FadeOut(glow_fade_duration, Easing.Out); + } + } + + private void flash() + { + var flash = new Box + { + RelativeSizeAxes = Axes.Both + }; + + colourContainer.Add(flash); + + flash.Colour = ButtonColour; + flash.Blending = BlendingMode.Additive; + flash.Alpha = 0.3f; + flash.FadeOutFromOne(click_duration); + flash.Expire(); + } + + private void updateGlow() + { + leftGlow.Colour = ColourInfo.GradientHorizontal(new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f), ButtonColour); + centerGlow.Colour = ButtonColour; + rightGlow.Colour = ColourInfo.GradientHorizontal(ButtonColour, new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f)); } } } From 9fb3d3704a7e0601006233ad20ac30ff5cf51f85 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 15:42:11 +0900 Subject: [PATCH 03/12] TestCaseMenuOverlays -> TestCaseMenuOverlay --- .../{TestCaseMenuOverlays.cs => TestCaseMenuOverlay.cs} | 8 ++++++-- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) rename osu.Game.Tests/Visual/{TestCaseMenuOverlays.cs => TestCaseMenuOverlay.cs} (83%) diff --git a/osu.Game.Tests/Visual/TestCaseMenuOverlays.cs b/osu.Game.Tests/Visual/TestCaseMenuOverlay.cs similarity index 83% rename from osu.Game.Tests/Visual/TestCaseMenuOverlays.cs rename to osu.Game.Tests/Visual/TestCaseMenuOverlay.cs index 94a69f0029..da846a1b39 100644 --- a/osu.Game.Tests/Visual/TestCaseMenuOverlays.cs +++ b/osu.Game.Tests/Visual/TestCaseMenuOverlay.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Collections.Generic; using System.ComponentModel; using osu.Framework.Graphics.Containers; using osu.Framework.Logging; @@ -9,9 +11,11 @@ using osu.Game.Screens.Play; namespace osu.Game.Tests.Visual { [Description("player pause/fail screens")] - internal class TestCaseMenuOverlays : OsuTestCase + internal class TestCaseMenuOverlay : OsuTestCase { - public TestCaseMenuOverlays() + public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer.PauseOverlay) }; + + public TestCaseMenuOverlay() { FailOverlay failOverlay; PauseContainer.PauseOverlay pauseOverlay; diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 184561faa7..cf91ca0c15 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -120,7 +120,7 @@ - + From 1e4cad900dd46cf2ce3d5509f5224695eb9dc32f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 16:04:51 +0900 Subject: [PATCH 04/12] Fix up incorrect RequiredTypes --- osu.Game.Tests/Visual/TestCaseMenuOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseMenuOverlay.cs b/osu.Game.Tests/Visual/TestCaseMenuOverlay.cs index da846a1b39..6c1c615ca9 100644 --- a/osu.Game.Tests/Visual/TestCaseMenuOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseMenuOverlay.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual [Description("player pause/fail screens")] internal class TestCaseMenuOverlay : OsuTestCase { - public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer.PauseOverlay) }; + public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) }; public TestCaseMenuOverlay() { From 59365bbdce1ec25feec558c6ab4f10ae6e0721d8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 16:29:40 +0900 Subject: [PATCH 05/12] Make MenuOverlay support key selections --- osu.Game/Screens/Play/MenuOverlay.cs | 72 ++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/MenuOverlay.cs b/osu.Game/Screens/Play/MenuOverlay.cs index 6d25c33243..452eaae574 100644 --- a/osu.Game/Screens/Play/MenuOverlay.cs +++ b/osu.Game/Screens/Play/MenuOverlay.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics; using osu.Framework.Allocation; using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Shapes; +using OpenTK.Input; namespace osu.Game.Screens.Play { @@ -37,6 +38,8 @@ namespace osu.Game.Screens.Play protected MenuOverlay() { RelativeSizeAxes = Axes.Both; + + StateChanged += s => selectionIndex = -1; } [BackgroundDependencyLoader] @@ -172,7 +175,7 @@ namespace osu.Game.Screens.Play protected void AddButton(string text, Color4 colour, Action action) { - Buttons.Add(new Button + var button = new MenuOverlayButton { Text = text, ButtonColour = colour, @@ -184,11 +187,74 @@ namespace osu.Game.Screens.Play action?.Invoke(); Hide(); } - }); + }; + + button.Selected.ValueChanged += s => buttonSelectionChanged(button, s); + + Buttons.Add(button); } - public class Button : DialogButton + private int _selectionIndex = -1; + private int selectionIndex { + get { return _selectionIndex; } + set + { + if (_selectionIndex == value) + return; + + if (_selectionIndex != -1) + Buttons[_selectionIndex].Selected.Value = false; + + _selectionIndex = value; + + if (_selectionIndex != -1) + Buttons[_selectionIndex].Selected.Value = true; + } + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat) + return false; + + switch (args.Key) + { + case Key.Up: + if (selectionIndex == -1 || selectionIndex == 0) + selectionIndex = Buttons.Count - 1; + else + selectionIndex--; + return true; + case Key.Down: + if (selectionIndex == -1 || selectionIndex == Buttons.Count - 1) + selectionIndex = 0; + else + selectionIndex++; + return true; + } + + return false; + } + + private void buttonSelectionChanged(DialogButton button, bool isSelected) + { + if (!isSelected) + selectionIndex = -1; + else + selectionIndex = Buttons.IndexOf(button); + } + + private class MenuOverlayButton : DialogButton + { + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat || args.Key != Key.Enter || !Selected) + return false; + + OnClick(state); + return true; + } } } } From 5f538f03eab7c0a70c811f71c2b5323f055c4786 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 16:34:25 +0900 Subject: [PATCH 06/12] Comments --- osu.Game/Screens/Play/MenuOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/MenuOverlay.cs b/osu.Game/Screens/Play/MenuOverlay.cs index 452eaae574..6e3c1a4d44 100644 --- a/osu.Game/Screens/Play/MenuOverlay.cs +++ b/osu.Game/Screens/Play/MenuOverlay.cs @@ -203,11 +203,13 @@ namespace osu.Game.Screens.Play if (_selectionIndex == value) return; + // Deselect the previously-selected button if (_selectionIndex != -1) Buttons[_selectionIndex].Selected.Value = false; _selectionIndex = value; + // Select the newly-selected button if (_selectionIndex != -1) Buttons[_selectionIndex].Selected.Value = true; } From 846750a1905315dcb7861edc6d01640221c1ae8d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 16:36:11 +0900 Subject: [PATCH 07/12] Remove unnecessary flag --- osu.Game/Graphics/UserInterface/DialogButton.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 3b3cb953de..320bf51aaf 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -35,8 +35,6 @@ namespace osu.Game.Graphics.UserInterface private readonly SpriteText spriteText; private Vector2 hoverSpacing => new Vector2(3f, 0f); - private bool didClick; // Used for making sure that the OnMouseDown animation can call instead of OnHoverLost's when clicking - public DialogButton() { RelativeSizeAxes = Axes.X; @@ -216,7 +214,6 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnClick(Framework.Input.InputState state) { - didClick = true; colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In); flash(); @@ -252,7 +249,7 @@ namespace osu.Game.Graphics.UserInterface colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, Easing.OutElastic); glowContainer.FadeIn(glow_fade_duration, Easing.Out); } - else if (!didClick) + else { colourContainer.ResizeTo(new Vector2(0.8f, 1f), hover_duration, Easing.OutElastic); spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic); From cf640d084e50b78b275df22503953b17ab154aec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 16:38:15 +0900 Subject: [PATCH 08/12] Use using --- osu.Game/Graphics/UserInterface/DialogButton.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 320bf51aaf..f07bc4114f 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Sprites; using osu.Framework.Extensions.Color4Extensions; using osu.Game.Graphics.Containers; using osu.Framework.Configuration; +using osu.Framework.Input; namespace osu.Game.Graphics.UserInterface { @@ -212,7 +213,7 @@ namespace osu.Game.Graphics.UserInterface public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceiveMouseInputAt(screenSpacePos); - protected override bool OnClick(Framework.Input.InputState state) + protected override bool OnClick(InputState state) { colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In); flash(); @@ -227,7 +228,7 @@ namespace osu.Game.Graphics.UserInterface return base.OnClick(state); } - protected override bool OnHover(Framework.Input.InputState state) + protected override bool OnHover(InputState state) { base.OnHover(state); @@ -235,7 +236,7 @@ namespace osu.Game.Graphics.UserInterface return true; } - protected override void OnHoverLost(Framework.Input.InputState state) + protected override void OnHoverLost(InputState state) { base.OnHoverLost(state); Selected.Value = false; From 918e7c9a4bc8ef7cd4e41dc1f169efc7fa85adb6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 16:40:50 +0900 Subject: [PATCH 09/12] MenuOverlay -> GameplayMenuOverlay --- ...tCaseMenuOverlay.cs => TestCaseGameplayMenuOverlay.cs} | 4 ++-- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/Screens/Play/FailOverlay.cs | 2 +- .../Play/{MenuOverlay.cs => GameplayMenuOverlay.cs} | 8 ++++---- osu.Game/Screens/Play/PauseContainer.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) rename osu.Game.Tests/Visual/{TestCaseMenuOverlay.cs => TestCaseGameplayMenuOverlay.cs} (91%) rename osu.Game/Screens/Play/{MenuOverlay.cs => GameplayMenuOverlay.cs} (94%) diff --git a/osu.Game.Tests/Visual/TestCaseMenuOverlay.cs b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs similarity index 91% rename from osu.Game.Tests/Visual/TestCaseMenuOverlay.cs rename to osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs index 6c1c615ca9..ecb107085a 100644 --- a/osu.Game.Tests/Visual/TestCaseMenuOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs @@ -11,11 +11,11 @@ using osu.Game.Screens.Play; namespace osu.Game.Tests.Visual { [Description("player pause/fail screens")] - internal class TestCaseMenuOverlay : OsuTestCase + internal class TestCaseGameplayMenuOverlay : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) }; - public TestCaseMenuOverlay() + public TestCaseGameplayMenuOverlay() { FailOverlay failOverlay; PauseContainer.PauseOverlay pauseOverlay; diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index cf91ca0c15..5442ce63dd 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -120,7 +120,7 @@ - + diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 3e31da2348..3242d8bb6e 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -10,7 +10,7 @@ using System.Linq; namespace osu.Game.Screens.Play { - public class FailOverlay : MenuOverlay + public class FailOverlay : GameplayMenuOverlay { public override string Header => "failed"; public override string Description => "you're dead, try again?"; diff --git a/osu.Game/Screens/Play/MenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs similarity index 94% rename from osu.Game/Screens/Play/MenuOverlay.cs rename to osu.Game/Screens/Play/GameplayMenuOverlay.cs index 6e3c1a4d44..996717cbb5 100644 --- a/osu.Game/Screens/Play/MenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -17,7 +17,7 @@ using OpenTK.Input; namespace osu.Game.Screens.Play { - public abstract class MenuOverlay : OverlayContainer, IRequireHighFrequencyMousePosition + public abstract class GameplayMenuOverlay : OverlayContainer, IRequireHighFrequencyMousePosition { private const int transition_duration = 200; private const int button_height = 70; @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play private FillFlowContainer retryCounterContainer; - protected MenuOverlay() + protected GameplayMenuOverlay() { RelativeSizeAxes = Axes.Both; @@ -175,7 +175,7 @@ namespace osu.Game.Screens.Play protected void AddButton(string text, Color4 colour, Action action) { - var button = new MenuOverlayButton + var button = new Button { Text = text, ButtonColour = colour, @@ -247,7 +247,7 @@ namespace osu.Game.Screens.Play selectionIndex = Buttons.IndexOf(button); } - private class MenuOverlayButton : DialogButton + private class Button : DialogButton { protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs index 5f5eeb63a0..6812ac5fc5 100644 --- a/osu.Game/Screens/Play/PauseContainer.cs +++ b/osu.Game/Screens/Play/PauseContainer.cs @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Play base.Update(); } - public class PauseOverlay : MenuOverlay + public class PauseOverlay : GameplayMenuOverlay { public Action OnResume; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c190d988fa..155f08fd66 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -719,7 +719,7 @@ - + From a17b2e4c1801388afb692b957a9aaff01be0c1fe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 19:13:08 +0900 Subject: [PATCH 10/12] Expose buttons for test cases --- osu.Game/Screens/Play/FailOverlay.cs | 2 +- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 18 ++++++++++-------- osu.Game/Screens/Play/PauseContainer.cs | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 3242d8bb6e..a6a233058b 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -26,7 +26,7 @@ namespace osu.Game.Screens.Play { if (!args.Repeat && args.Key == Key.Escape) { - Buttons.Children.Last().TriggerOnClick(); + InternalButtons.Children.Last().TriggerOnClick(); return true; } diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 996717cbb5..182c4efe89 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -14,6 +14,7 @@ using osu.Framework.Allocation; using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Shapes; using OpenTK.Input; +using System.Collections.Generic; namespace osu.Game.Screens.Play { @@ -31,7 +32,8 @@ namespace osu.Game.Screens.Play public abstract string Header { get; } public abstract string Description { get; } - protected FillFlowContainer Buttons; + protected internal FillFlowContainer InternalButtons; + public IReadOnlyList Buttons => InternalButtons; private FillFlowContainer retryCounterContainer; @@ -95,7 +97,7 @@ namespace osu.Game.Screens.Play } } }, - Buttons = new FillFlowContainer + InternalButtons = new FillFlowContainer { Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, @@ -191,7 +193,7 @@ namespace osu.Game.Screens.Play button.Selected.ValueChanged += s => buttonSelectionChanged(button, s); - Buttons.Add(button); + InternalButtons.Add(button); } private int _selectionIndex = -1; @@ -205,13 +207,13 @@ namespace osu.Game.Screens.Play // Deselect the previously-selected button if (_selectionIndex != -1) - Buttons[_selectionIndex].Selected.Value = false; + InternalButtons[_selectionIndex].Selected.Value = false; _selectionIndex = value; // Select the newly-selected button if (_selectionIndex != -1) - Buttons[_selectionIndex].Selected.Value = true; + InternalButtons[_selectionIndex].Selected.Value = true; } } @@ -224,12 +226,12 @@ namespace osu.Game.Screens.Play { case Key.Up: if (selectionIndex == -1 || selectionIndex == 0) - selectionIndex = Buttons.Count - 1; + selectionIndex = InternalButtons.Count - 1; else selectionIndex--; return true; case Key.Down: - if (selectionIndex == -1 || selectionIndex == Buttons.Count - 1) + if (selectionIndex == -1 || selectionIndex == InternalButtons.Count - 1) selectionIndex = 0; else selectionIndex++; @@ -244,7 +246,7 @@ namespace osu.Game.Screens.Play if (!isSelected) selectionIndex = -1; else - selectionIndex = Buttons.IndexOf(button); + selectionIndex = InternalButtons.IndexOf(button); } private class Button : DialogButton diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs index 6812ac5fc5..8545c50536 100644 --- a/osu.Game/Screens/Play/PauseContainer.cs +++ b/osu.Game/Screens/Play/PauseContainer.cs @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Play { if (!args.Repeat && args.Key == Key.Escape) { - Buttons.Children.First().TriggerOnClick(); + InternalButtons.Children.First().TriggerOnClick(); return true; } From 5e111e14db9845f48dafa9264d100dce60d0c214 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 19:13:25 +0900 Subject: [PATCH 11/12] Make it possible to change the overlay actions beyond instantiation --- osu.Game/Screens/Play/FailOverlay.cs | 4 ++-- osu.Game/Screens/Play/PauseContainer.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index a6a233058b..09f2e15c57 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -18,8 +18,8 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load(OsuColour colours) { - AddButton("Retry", colours.YellowDark, OnRetry); - AddButton("Quit", new Color4(170, 27, 39, 255), OnQuit); + AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); + AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); } protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs index 8545c50536..3bd28511c7 100644 --- a/osu.Game/Screens/Play/PauseContainer.cs +++ b/osu.Game/Screens/Play/PauseContainer.cs @@ -140,9 +140,9 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load(OsuColour colours) { - AddButton("Continue", colours.Green, OnResume); - AddButton("Retry", colours.YellowDark, OnRetry); - AddButton("Quit", new Color4(170, 27, 39, 255), OnQuit); + AddButton("Continue", colours.Green, () => OnResume?.Invoke()); + AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); + AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); } } } From f90e3346c1f8f836f1f3d084143c693668828a56 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Dec 2017 19:13:33 +0900 Subject: [PATCH 12/12] Add automated test cases --- .../Visual/TestCaseGameplayMenuOverlay.cs | 247 ++++++++++++++++-- 1 file changed, 222 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs index ecb107085a..8389037a71 100644 --- a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs @@ -4,7 +4,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; +using OpenTK.Input; +using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; +using osu.Framework.Input; using osu.Framework.Logging; using osu.Game.Screens.Play; @@ -15,47 +19,240 @@ namespace osu.Game.Tests.Visual { public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) }; - public TestCaseGameplayMenuOverlay() + private FailOverlay failOverlay; + private PauseContainer.PauseOverlay pauseOverlay; + + [BackgroundDependencyLoader] + private void load() { - FailOverlay failOverlay; - PauseContainer.PauseOverlay pauseOverlay; - - var retryCount = 0; - Add(pauseOverlay = new PauseContainer.PauseOverlay { OnResume = () => Logger.Log(@"Resume"), OnRetry = () => Logger.Log(@"Retry"), OnQuit = () => Logger.Log(@"Quit"), }); + Add(failOverlay = new FailOverlay { OnRetry = () => Logger.Log(@"Retry"), OnQuit = () => Logger.Log(@"Quit"), }); - AddStep(@"Pause", delegate - { - if (failOverlay.State == Visibility.Visible) - { - failOverlay.Hide(); - } - pauseOverlay.Show(); - }); - AddStep("Fail", delegate - { - if (pauseOverlay.State == Visibility.Visible) - { - pauseOverlay.Hide(); - } - failOverlay.Show(); - }); - AddStep("Add Retry", delegate + var retryCount = 0; + + AddStep("Add retry", () => { retryCount++; - pauseOverlay.Retries = retryCount; - failOverlay.Retries = retryCount; + pauseOverlay.Retries = failOverlay.Retries = retryCount; }); + + AddToggleStep("Toggle pause overlay", t => pauseOverlay.ToggleVisibility()); + AddToggleStep("Toggle fail overlay", t => failOverlay.ToggleVisibility()); + + testHideResets(); + + testEnterWithoutSelection(); + testKeyUpFromInitial(); + testKeyDownFromInitial(); + testKeyUpWrapping(); + testKeyDownWrapping(); + + testMouseSelectionAfterKeySelection(); + testKeySelectionAfterMouseSelection(); + + testMouseDeselectionResets(); + + testClickSelection(); + testEnterKeySelection(); + } + + /// + /// Test that hiding the overlay after hovering a button will reset the overlay to the initial state with no buttons selected. + /// + private void testHideResets() + { + AddStep("Show overlay", () => failOverlay.Show()); + + AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnHover(null)); + AddStep("Hide overlay", () => failOverlay.Hide()); + + AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected)); + } + + /// + /// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred. + /// + private void testEnterWithoutSelection() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + AddStep("Press enter", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter })); + AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that pressing the up arrow from the initial state selects the last button. + /// + private void testKeyUpFromInitial() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that pressing the down arrow from the initial state selects the first button. + /// + private void testKeyDownFromInitial() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that pressing the up arrow repeatedly causes the selected button to wrap correctly. + /// + private void testKeyUpWrapping() + { + AddStep("Show overlay", () => failOverlay.Show()); + + AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); + AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); + AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); + + AddStep("Hide overlay", () => failOverlay.Hide()); + } + + /// + /// Tests that pressing the down arrow repeatedly causes the selected button to wrap correctly. + /// + private void testKeyDownWrapping() + { + AddStep("Show overlay", () => failOverlay.Show()); + + AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); + AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); + AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); + + AddStep("Hide overlay", () => failOverlay.Hide()); + } + + /// + /// Tests that hovering a button that was previously selected with the keyboard correctly selects the new button and deselects the previous button. + /// + private void testMouseSelectionAfterKeySelection() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + var secondButton = pauseOverlay.Buttons.Skip(1).First(); + + AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddStep("Hover second button", () => secondButton.TriggerOnHover(null)); + AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected); + AddAssert("Second button selected", () => secondButton.Selected); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that pressing a key after selecting a button with a hover event correctly selects a new button and deselects the previous button. + /// + private void testKeySelectionAfterMouseSelection() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + var secondButton = pauseOverlay.Buttons.Skip(1).First(); + + AddStep("Hover second button", () => secondButton.TriggerOnHover(null)); + AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddAssert("Second button not selected", () => !secondButton.Selected); + AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that deselecting with the mouse by losing hover will reset the overlay to the initial state. + /// + private void testMouseDeselectionResets() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + var secondButton = pauseOverlay.Buttons.Skip(1).First(); + + AddStep("Hover second button", () => secondButton.TriggerOnHover(null)); + AddStep("Unhover second button", () => secondButton.TriggerOnHoverLost(null)); + AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition + + AddStep("Hide overlay", () => pauseOverlay.Hide()); + } + + /// + /// Tests that clicking on a button correctly causes a click event for that button. + /// + private void testClickSelection() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + var retryButton = pauseOverlay.Buttons.Skip(1).First(); + + bool triggered = false; + AddStep("Click retry button", () => + { + var lastAction = pauseOverlay.OnRetry; + pauseOverlay.OnRetry = () => triggered = true; + + retryButton.TriggerOnClick(); + pauseOverlay.OnRetry = lastAction; + }); + + AddAssert("Action was triggered", () => triggered); + AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden); + } + + /// + /// Tests that pressing the enter key with a button selected correctly causes a click event for that button. + /// + private void testEnterKeySelection() + { + AddStep("Show overlay", () => pauseOverlay.Show()); + + AddStep("Select second button", () => + { + pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }); + pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }); + }); + + var retryButton = pauseOverlay.Buttons.Skip(1).First(); + + bool triggered = false; + AddStep("Press enter", () => + { + var lastAction = pauseOverlay.OnRetry; + pauseOverlay.OnRetry = () => triggered = true; + + retryButton.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter }); + pauseOverlay.OnRetry = lastAction; + }); + + AddAssert("Action was triggered", () => triggered); + AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden); } } }