From 177a789d792b55d69a232c56d82a2813ea6f0159 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 14:04:51 +0900 Subject: [PATCH 1/7] Add setting to adjust hold-to-confirm activation time --- osu.Game/Configuration/OsuConfigManager.cs | 5 ++++- .../Containers/HoldToConfirmContainer.cs | 18 ++++++++---------- osu.Game/Overlays/HoldToConfirmOverlay.cs | 2 +- .../Sections/Graphics/UserInterfaceSettings.cs | 15 ++++++++++++++- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index e26021d930..62590a0a8f 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -112,6 +112,8 @@ namespace osu.Game.Configuration Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f); + Set(OsuSetting.UIHoldActivationDelay, 200, 0, 500); + Set(OsuSetting.IntroSequence, IntroSequence.Triangles); } @@ -180,6 +182,7 @@ namespace osu.Game.Configuration ScalingSizeX, ScalingSizeY, UIScale, - IntroSequence + IntroSequence, + UIHoldActivationDelay } } diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index 773265d19b..a345fb554f 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -2,9 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Configuration; namespace osu.Game.Graphics.Containers { @@ -12,11 +14,8 @@ namespace osu.Game.Graphics.Containers { public Action Action; - private const int default_activation_delay = 200; private const int fadeout_delay = 200; - private readonly double activationDelay; - private bool fired; private bool confirming; @@ -27,13 +26,12 @@ namespace osu.Game.Graphics.Containers public Bindable Progress = new BindableDouble(); - /// - /// Create a new instance. - /// - /// The time requried before an action is confirmed. - protected HoldToConfirmContainer(double activationDelay = default_activation_delay) + private Bindable holdActivationDelay; + + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) { - this.activationDelay = activationDelay; + holdActivationDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); } protected void BeginConfirm() @@ -42,7 +40,7 @@ namespace osu.Game.Graphics.Containers confirming = true; - this.TransformBindableTo(Progress, 1, activationDelay * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm()); + this.TransformBindableTo(Progress, 1, holdActivationDelay.Value * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm()); } protected virtual void Confirm() diff --git a/osu.Game/Overlays/HoldToConfirmOverlay.cs b/osu.Game/Overlays/HoldToConfirmOverlay.cs index fdc6f096bc..eb325d8dd3 100644 --- a/osu.Game/Overlays/HoldToConfirmOverlay.cs +++ b/osu.Game/Overlays/HoldToConfirmOverlay.cs @@ -51,7 +51,7 @@ namespace osu.Game.Overlays protected override void Dispose(bool isDisposing) { - audio.Tracks.RemoveAdjustment(AdjustableProperty.Volume, audioVolume); + audio?.Tracks.RemoveAdjustment(AdjustableProperty.Volume, audioVolume); base.Dispose(isDisposing); } } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs index dd822fedb6..a6956b7d9a 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/UserInterfaceSettings.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings.Sections.Graphics { @@ -13,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - Children = new[] + Children = new Drawable[] { new SettingsCheckbox { @@ -25,7 +27,18 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics LabelText = "Parallax", Bindable = config.GetBindable(OsuSetting.MenuParallax) }, + new SettingsSlider + { + LabelText = "Hold-to-confirm activation time", + Bindable = config.GetBindable(OsuSetting.UIHoldActivationDelay), + KeyboardStep = 50 + }, }; } + + private class TimeSlider : OsuSliderBar + { + public override string TooltipText => Current.Value.ToString("N0") + "ms"; + } } } From a214e7e72fbf131c001b07b1f9288764bd5cdf85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 16:26:22 +0900 Subject: [PATCH 2/7] Add confirmation dialog when exiting game --- osu.Game/Screens/Menu/MainMenu.cs | 49 ++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index a006877082..9393f785cd 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -1,18 +1,22 @@ // 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 osuTK; using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Overlays; +using osu.Game.Overlays.Dialog; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Charts; using osu.Game.Screens.Edit; @@ -51,16 +55,23 @@ namespace osu.Game.Screens.Menu [Resolved] private IAPIProvider api { get; set; } + [Resolved] + private DialogOverlay dialogOverlay { get; set; } + private BackgroundScreenDefault background; protected override BackgroundScreen CreateBackground() => background; + private Bindable holdDelay; + [BackgroundDependencyLoader(true)] - private void load(DirectOverlay direct, SettingsOverlay settings) + private void load(DirectOverlay direct, SettingsOverlay settings, OsuConfigManager config) { if (host.CanExit) AddInternal(new ExitConfirmOverlay { Action = this.Exit }); + holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); + AddRangeInternal(new Drawable[] { new ParallaxContainer @@ -141,6 +152,7 @@ namespace osu.Game.Screens.Menu } private bool loginDisplayed; + private bool exitConfirmed; protected override void LogoArriving(OsuLogo logo, bool resuming) { @@ -221,9 +233,44 @@ namespace osu.Game.Screens.Menu public override bool OnExiting(IScreen next) { + if (holdDelay.Value == 0 && !exitConfirmed) + { + dialogOverlay?.Push(new ConfirmExitDialog(() => + { + exitConfirmed = true; + this.Exit(); + })); + + return true; + } + buttons.State = ButtonSystemState.Exit; this.FadeOut(3000); return base.OnExiting(next); } + + public class ConfirmExitDialog : PopupDialog + { + public ConfirmExitDialog(Action confirm) + { + HeaderText = "Are you sure you want to exit?"; + BodyText = "Last chance to back out."; + + Icon = FontAwesome.Solid.ExclamationTriangle; + + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = @"Good bye", + Action = confirm + }, + new PopupDialogCancelButton + { + Text = @"Just a little more" + }, + }; + } + } } } From 929f05884b8bf5cd4ce70c9481ffabf0486174e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 16:28:06 +0900 Subject: [PATCH 3/7] Always confirm exit when button is clicked --- osu.Game/Screens/Menu/MainMenu.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 9393f785cd..7645734859 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -85,7 +85,11 @@ namespace osu.Game.Screens.Menu OnEdit = delegate { this.Push(new Editor()); }, OnSolo = onSolo, OnMulti = delegate { this.Push(new Multiplayer()); }, - OnExit = this.Exit, + OnExit = delegate + { + exitConfirmed = true; + this.Exit(); + }, } } }, From fa54a0bfd3954576adc0a07fef7619d6d6050f3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 17:40:46 +0900 Subject: [PATCH 4/7] Fix test failures --- osu.Game/Screens/Menu/MainMenu.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 7645734859..2d8c48873a 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Menu [Resolved] private IAPIProvider api { get; set; } - [Resolved] + [Resolved(canBeNull: true)] private DialogOverlay dialogOverlay { get; set; } private BackgroundScreenDefault background; @@ -237,9 +237,9 @@ namespace osu.Game.Screens.Menu public override bool OnExiting(IScreen next) { - if (holdDelay.Value == 0 && !exitConfirmed) + if (holdDelay.Value == 0 && !exitConfirmed && dialogOverlay != null) { - dialogOverlay?.Push(new ConfirmExitDialog(() => + dialogOverlay.Push(new ConfirmExitDialog(() => { exitConfirmed = true; this.Exit(); From 50d4206c4584950d20e8824585a613a5a6b5ada2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 20:17:58 +0900 Subject: [PATCH 5/7] Fix exit scenarios --- .../TestSceneHoldToConfirmOverlay.cs | 3 --- .../Containers/HoldToConfirmContainer.cs | 13 +++++++--- osu.Game/Overlays/DialogOverlay.cs | 26 +++++++++++-------- osu.Game/Screens/Menu/ExitConfirmOverlay.cs | 7 ++++- osu.Game/Screens/Menu/MainMenu.cs | 16 ++++++++---- 5 files changed, 41 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs index f787754aa4..d4143f3f8d 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs @@ -61,10 +61,7 @@ namespace osu.Game.Tests.Visual.UserInterface private class TestHoldToConfirmOverlay : ExitConfirmOverlay { - protected override bool AllowMultipleFires => true; - public void Begin() => BeginConfirm(); - public void Abort() => AbortConfirm(); } } } diff --git a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs index a345fb554f..5d549ba217 100644 --- a/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs +++ b/osu.Game/Graphics/Containers/HoldToConfirmContainer.cs @@ -16,7 +16,11 @@ namespace osu.Game.Graphics.Containers private const int fadeout_delay = 200; - private bool fired; + /// + /// Whether currently in a fired state (and the confirm has been sent). + /// + public bool Fired { get; private set; } + private bool confirming; /// @@ -36,7 +40,7 @@ namespace osu.Game.Graphics.Containers protected void BeginConfirm() { - if (confirming || (!AllowMultipleFires && fired)) return; + if (confirming || (!AllowMultipleFires && Fired)) return; confirming = true; @@ -46,14 +50,15 @@ namespace osu.Game.Graphics.Containers protected virtual void Confirm() { Action?.Invoke(); - fired = true; + Fired = true; } protected void AbortConfirm() { - if (!AllowMultipleFires && fired) return; + if (!AllowMultipleFires && Fired) return; confirming = false; + Fired = false; this.TransformBindableTo(Progress, 0, fadeout_delay, Easing.Out); } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 6aaeff8554..59d748bc5d 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -13,7 +13,8 @@ namespace osu.Game.Overlays public class DialogOverlay : OsuFocusedOverlayContainer { private readonly Container dialogContainer; - private PopupDialog currentDialog; + + public PopupDialog CurrentDialog { get; private set; } public DialogOverlay() { @@ -31,15 +32,15 @@ namespace osu.Game.Overlays public void Push(PopupDialog dialog) { - if (dialog == currentDialog) return; + if (dialog == CurrentDialog) return; - currentDialog?.Hide(); - currentDialog = dialog; + CurrentDialog?.Hide(); + CurrentDialog = dialog; - dialogContainer.Add(currentDialog); + dialogContainer.Add(CurrentDialog); - currentDialog.Show(); - currentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue); + CurrentDialog.Show(); + CurrentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue); Show(); } @@ -52,8 +53,11 @@ namespace osu.Game.Overlays //handle the dialog being dismissed. dialog.Delay(PopupDialog.EXIT_DURATION).Expire(); - if (dialog == currentDialog) + if (dialog == CurrentDialog) + { Hide(); + CurrentDialog = null; + } } protected override void PopIn() @@ -66,9 +70,9 @@ namespace osu.Game.Overlays { base.PopOut(); - if (currentDialog?.State.Value == Visibility.Visible) + if (CurrentDialog?.State.Value == Visibility.Visible) { - currentDialog.Hide(); + CurrentDialog.Hide(); return; } @@ -80,7 +84,7 @@ namespace osu.Game.Overlays switch (action) { case GlobalAction.Select: - currentDialog?.Buttons.OfType().FirstOrDefault()?.Click(); + CurrentDialog?.Buttons.OfType().FirstOrDefault()?.Click(); return true; } diff --git a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs index 519834859d..aaa3a77e74 100644 --- a/osu.Game/Screens/Menu/ExitConfirmOverlay.cs +++ b/osu.Game/Screens/Menu/ExitConfirmOverlay.cs @@ -9,6 +9,10 @@ namespace osu.Game.Screens.Menu { public class ExitConfirmOverlay : HoldToConfirmOverlay, IKeyBindingHandler { + protected override bool AllowMultipleFires => true; + + public void Abort() => AbortConfirm(); + public bool OnPressed(GlobalAction action) { if (action == GlobalAction.Back) @@ -24,7 +28,8 @@ namespace osu.Game.Screens.Menu { if (action == GlobalAction.Back) { - AbortConfirm(); + if (!Fired) + AbortConfirm(); return true; } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 2d8c48873a..23fb2d8e37 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -64,11 +64,13 @@ namespace osu.Game.Screens.Menu private Bindable holdDelay; + private ExitConfirmOverlay exitConfirmOverlay; + [BackgroundDependencyLoader(true)] private void load(DirectOverlay direct, SettingsOverlay settings, OsuConfigManager config) { if (host.CanExit) - AddInternal(new ExitConfirmOverlay { Action = this.Exit }); + AddInternal(exitConfirmOverlay = new ExitConfirmOverlay { Action = this.Exit }); holdDelay = config.GetBindable(OsuSetting.UIHoldActivationDelay); @@ -237,12 +239,15 @@ namespace osu.Game.Screens.Menu public override bool OnExiting(IScreen next) { - if (holdDelay.Value == 0 && !exitConfirmed && dialogOverlay != null) + if (holdDelay.Value == 0 && !exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) { dialogOverlay.Push(new ConfirmExitDialog(() => { exitConfirmed = true; this.Exit(); + }, () => + { + exitConfirmOverlay.Abort(); })); return true; @@ -253,9 +258,9 @@ namespace osu.Game.Screens.Menu return base.OnExiting(next); } - public class ConfirmExitDialog : PopupDialog + private class ConfirmExitDialog : PopupDialog { - public ConfirmExitDialog(Action confirm) + public ConfirmExitDialog(Action confirm, Action cancel) { HeaderText = "Are you sure you want to exit?"; BodyText = "Last chance to back out."; @@ -271,7 +276,8 @@ namespace osu.Game.Screens.Menu }, new PopupDialogCancelButton { - Text = @"Just a little more" + Text = @"Just a little more", + Action = cancel }, }; } From 23c5cb6367e1cf976eaae4cec6f6662b48adc9e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 22:35:14 +0900 Subject: [PATCH 6/7] Expand tests to cover new behaviour --- .../Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs index d4143f3f8d..feef1dae6b 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneHoldToConfirmOverlay.cs @@ -52,11 +52,18 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("start confirming", () => overlay.Begin()); AddStep("abort confirming", () => overlay.Abort()); + AddAssert("ensure not fired internally", () => !overlay.Fired); AddAssert("ensure aborted", () => !fired); AddStep("start confirming", () => overlay.Begin()); AddUntilStep("wait until confirmed", () => fired); + AddAssert("ensure fired internally", () => overlay.Fired); + + AddStep("abort after fire", () => overlay.Abort()); + AddAssert("ensure not fired internally", () => !overlay.Fired); + AddStep("start confirming", () => overlay.Begin()); + AddUntilStep("wait until fired again", () => overlay.Fired); } private class TestHoldToConfirmOverlay : ExitConfirmOverlay From 67796e098258dd194367aa2bf031c66064b205cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 19 Sep 2019 22:46:21 +0900 Subject: [PATCH 7/7] Apply code styling suggestions --- osu.Game/Screens/Menu/MainMenu.cs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 23fb2d8e37..0274973161 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -87,11 +87,7 @@ namespace osu.Game.Screens.Menu OnEdit = delegate { this.Push(new Editor()); }, OnSolo = onSolo, OnMulti = delegate { this.Push(new Multiplayer()); }, - OnExit = delegate - { - exitConfirmed = true; - this.Exit(); - }, + OnExit = confirmAndExit, } } }, @@ -120,6 +116,12 @@ namespace osu.Game.Screens.Menu preloadSongSelect(); } + private void confirmAndExit() + { + exitConfirmed = true; + this.Exit(); + } + private void preloadSongSelect() { if (songSelect == null) @@ -241,15 +243,7 @@ namespace osu.Game.Screens.Menu { if (holdDelay.Value == 0 && !exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog)) { - dialogOverlay.Push(new ConfirmExitDialog(() => - { - exitConfirmed = true; - this.Exit(); - }, () => - { - exitConfirmOverlay.Abort(); - })); - + dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort())); return true; }