From 3c0ce2b6153e1e3185edef81090b2120a99798c6 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Mon, 19 Nov 2018 20:48:59 +0300 Subject: [PATCH 1/9] Revert "Revert "Merge pull request #3415 from UselessToucan/return_to_large_logo_after_idle"" This reverts commit bcdaee7d395f56c47dd67f6fc517edf4463daa4d. --- osu.Game/Input/IdleTracker.cs | 41 +++++++++++++++++++++++++++ osu.Game/OsuGame.cs | 5 ++++ osu.Game/Screens/Menu/ButtonSystem.cs | 10 +++++-- 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Input/IdleTracker.cs diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs new file mode 100644 index 0000000000..bbc15fe7af --- /dev/null +++ b/osu.Game/Input/IdleTracker.cs @@ -0,0 +1,41 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; + +namespace osu.Game.Input +{ + public class IdleTracker : Component, IKeyBindingHandler + { + private double lastInteractionTime; + public double IdleTime => Clock.CurrentTime - lastInteractionTime; + + private bool updateLastInteractionTime() + { + lastInteractionTime = Clock.CurrentTime; + return false; + } + + public bool OnPressed(PlatformAction action) => updateLastInteractionTime(); + + public bool OnReleased(PlatformAction action) => updateLastInteractionTime(); + + protected override bool Handle(UIEvent e) + { + switch (e) + { + case KeyDownEvent _: + case KeyUpEvent _: + case MouseDownEvent _: + case MouseUpEvent _: + case MouseMoveEvent _: + return updateLastInteractionTime(); + default: + return base.Handle(e); + } + } + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 76a9102c5e..bb163e9870 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -26,6 +26,7 @@ using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Input; using osu.Game.Rulesets.Scoring; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; @@ -86,6 +87,8 @@ namespace osu.Game public float ToolbarOffset => Toolbar.Position.Y + Toolbar.DrawHeight; + private IdleTracker idleTracker; + public readonly Bindable OverlayActivationMode = new Bindable(); private OsuScreen screenStack; @@ -311,6 +314,7 @@ namespace osu.Game }, mainContent = new Container { RelativeSizeAxes = Axes.Both }, overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, + idleTracker = new IdleTracker { RelativeSizeAxes = Axes.Both } }); loadComponentSingleFile(screenStack = new Loader(), d => @@ -372,6 +376,7 @@ namespace osu.Game Depth = -6, }, overlayContent.Add); + dependencies.Cache(idleTracker); dependencies.Cache(settings); dependencies.Cache(onscreenDisplay); dependencies.Cache(social); diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 5c17317fc1..bb29a23637 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -14,6 +14,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Logging; using osu.Framework.Threading; using osu.Game.Graphics; +using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.Overlays; using OpenTK; @@ -64,6 +65,8 @@ namespace osu.Game.Screens.Menu private SampleChannel sampleBack; + private IdleTracker idleTracker; + public ButtonSystem() { RelativeSizeAxes = Axes.Both; @@ -102,9 +105,10 @@ namespace osu.Game.Screens.Menu private OsuGame game; [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, OsuGame game) + private void load(AudioManager audio, OsuGame game, IdleTracker idleTracker) { this.game = game; + this.idleTracker = idleTracker; sampleBack = audio.Sample.Get(@"Menu/button-back-select"); } @@ -266,8 +270,8 @@ namespace osu.Game.Screens.Menu protected override void Update() { - //if (OsuGame.IdleTime > 6000 && State != MenuState.Exit) - // State = MenuState.Initial; + if (idleTracker?.IdleTime > 6000 && State != ButtonSystemState.Exit) + State = ButtonSystemState.Initial; base.Update(); From 285b199d7d82a327bb4f5a125bc4cc87223c2b85 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Mon, 19 Nov 2018 20:50:46 +0300 Subject: [PATCH 2/9] Make IdleTracker IHandleGlobalInput --- osu.Game/Input/IdleTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs index bbc15fe7af..64792647c2 100644 --- a/osu.Game/Input/IdleTracker.cs +++ b/osu.Game/Input/IdleTracker.cs @@ -8,7 +8,7 @@ using osu.Framework.Input.Events; namespace osu.Game.Input { - public class IdleTracker : Component, IKeyBindingHandler + public class IdleTracker : Component, IKeyBindingHandler, IHandleGlobalInput { private double lastInteractionTime; public double IdleTime => Clock.CurrentTime - lastInteractionTime; From 8d65d491263c45bc00006b06edbcb0cbba075ee5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Nov 2018 16:32:59 +0900 Subject: [PATCH 3/9] Use bindable flow for checking idle time --- osu.Game/Input/IdleTracker.cs | 29 ++++++++++++++++++++++++++- osu.Game/OsuGame.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 19 ++++++++++++------ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs index 64792647c2..23ff75df4c 100644 --- a/osu.Game/Input/IdleTracker.cs +++ b/osu.Game/Input/IdleTracker.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Bindings; @@ -8,10 +9,30 @@ using osu.Framework.Input.Events; namespace osu.Game.Input { + /// + /// Track whether the end-user is in an idle state, based on their last interaction with the game. + /// public class IdleTracker : Component, IKeyBindingHandler, IHandleGlobalInput { + private readonly double timeToIdle; + private double lastInteractionTime; - public double IdleTime => Clock.CurrentTime - lastInteractionTime; + + protected double TimeSpentIdle => Clock.CurrentTime - lastInteractionTime; + + /// + /// Whether the user is currently in an idle state. + /// + public BindableBool IsIdle = new BindableBool(); + + /// + /// Intstantiate a new . + /// + /// The length in milliseconds until an idle state should be assumed. + public IdleTracker(double timeToIdle) + { + this.timeToIdle = timeToIdle; + } private bool updateLastInteractionTime() { @@ -19,6 +40,12 @@ namespace osu.Game.Input return false; } + protected override void Update() + { + base.Update(); + IsIdle.Value = TimeSpentIdle > timeToIdle; + } + public bool OnPressed(PlatformAction action) => updateLastInteractionTime(); public bool OnReleased(PlatformAction action) => updateLastInteractionTime(); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index aaf8622f0e..207fda7d74 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -319,7 +319,7 @@ namespace osu.Game }, mainContent = new Container { RelativeSizeAxes = Axes.Both }, overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, - idleTracker = new IdleTracker { RelativeSizeAxes = Axes.Both } + idleTracker = new IdleTracker(6000) { RelativeSizeAxes = Axes.Both } }); loadComponentSingleFile(screenStack = new Loader(), d => diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 9b7ad559e6..4ee4178770 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -8,6 +8,7 @@ using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; @@ -27,6 +28,8 @@ namespace osu.Game.Screens.Menu { public event Action StateChanged; + private readonly BindableBool isIdle = new BindableBool(); + public Action OnEdit; public Action OnExit; public Action OnDirect; @@ -65,8 +68,6 @@ namespace osu.Game.Screens.Menu private SampleChannel sampleBack; - private IdleTracker idleTracker; - public ButtonSystem() { RelativeSizeAxes = Axes.Both; @@ -108,10 +109,19 @@ namespace osu.Game.Screens.Menu private void load(AudioManager audio, OsuGame game, IdleTracker idleTracker) { this.game = game; - this.idleTracker = idleTracker; + + isIdle.ValueChanged += updateIdleState; + isIdle.BindTo(idleTracker.IsIdle); + sampleBack = audio.Sample.Get(@"Menu/button-back-select"); } + private void updateIdleState(bool isIdle) + { + if (isIdle && State != ButtonSystemState.Exit) + State = ButtonSystemState.Initial; + } + public bool OnPressed(GlobalAction action) { switch (action) @@ -270,9 +280,6 @@ namespace osu.Game.Screens.Menu protected override void Update() { - if (idleTracker?.IdleTime > 6000 && State != ButtonSystemState.Exit) - State = ButtonSystemState.Initial; - base.Update(); if (logo != null) From 68f0d255505cef90807e4ea8ed00c166c5ac8ac1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Nov 2018 16:33:58 +0900 Subject: [PATCH 4/9] Mvoe private method to bottom --- osu.Game/Input/IdleTracker.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs index 23ff75df4c..52bdbdc1b7 100644 --- a/osu.Game/Input/IdleTracker.cs +++ b/osu.Game/Input/IdleTracker.cs @@ -34,12 +34,6 @@ namespace osu.Game.Input this.timeToIdle = timeToIdle; } - private bool updateLastInteractionTime() - { - lastInteractionTime = Clock.CurrentTime; - return false; - } - protected override void Update() { base.Update(); @@ -64,5 +58,11 @@ namespace osu.Game.Input return base.Handle(e); } } + + private bool updateLastInteractionTime() + { + lastInteractionTime = Clock.CurrentTime; + return false; + } } } From 87da7b05a6b2ffad0008250522e8b4b3ad8b73dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Nov 2018 16:39:11 +0900 Subject: [PATCH 5/9] Remove unnecessarily specified RelativeSizeAxes --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 207fda7d74..d91f96db53 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -319,7 +319,7 @@ namespace osu.Game }, mainContent = new Container { RelativeSizeAxes = Axes.Both }, overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, - idleTracker = new IdleTracker(6000) { RelativeSizeAxes = Axes.Both } + idleTracker = new IdleTracker(6000) }); loadComponentSingleFile(screenStack = new Loader(), d => From 626048038cac091c1b565c02afef2fba327fd90b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Nov 2018 16:50:41 +0900 Subject: [PATCH 6/9] Handle the null case --- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 4ee4178770..5c67451c41 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Menu this.game = game; isIdle.ValueChanged += updateIdleState; - isIdle.BindTo(idleTracker.IsIdle); + if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle); sampleBack = audio.Sample.Get(@"Menu/button-back-select"); } From 266873740dce04da86f32b0658f26e0f3a026266 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Nov 2018 17:07:20 +0900 Subject: [PATCH 7/9] Specify RelativeSizeAxes in ctor This is basically always the behaviour we want. --- osu.Game/Input/IdleTracker.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs index 52bdbdc1b7..353b9bfba2 100644 --- a/osu.Game/Input/IdleTracker.cs +++ b/osu.Game/Input/IdleTracker.cs @@ -32,6 +32,7 @@ namespace osu.Game.Input public IdleTracker(double timeToIdle) { this.timeToIdle = timeToIdle; + RelativeSizeAxes = Axes.Both; } protected override void Update() From 32b36f28836e74311df166e0688c21b0b2b15221 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Nov 2018 17:07:30 +0900 Subject: [PATCH 8/9] Use IBindable<> --- osu.Game/Input/IdleTracker.cs | 6 ++++-- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/IdleTracker.cs b/osu.Game/Input/IdleTracker.cs index 353b9bfba2..d96fa8bd34 100644 --- a/osu.Game/Input/IdleTracker.cs +++ b/osu.Game/Input/IdleTracker.cs @@ -23,7 +23,9 @@ namespace osu.Game.Input /// /// Whether the user is currently in an idle state. /// - public BindableBool IsIdle = new BindableBool(); + public IBindable IsIdle => isIdle; + + private readonly BindableBool isIdle = new BindableBool(); /// /// Intstantiate a new . @@ -38,7 +40,7 @@ namespace osu.Game.Input protected override void Update() { base.Update(); - IsIdle.Value = TimeSpentIdle > timeToIdle; + isIdle.Value = TimeSpentIdle > timeToIdle; } public bool OnPressed(PlatformAction action) => updateLastInteractionTime(); diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 5c67451c41..ae1f27610b 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Menu { public event Action StateChanged; - private readonly BindableBool isIdle = new BindableBool(); + private readonly IBindable isIdle = new BindableBool(); public Action OnEdit; public Action OnExit; From bf2ecef6d8c1cbf28392105e3be43e03e8ed9c3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Nov 2018 17:40:25 +0900 Subject: [PATCH 9/9] Add comprehensive tests --- osu.Game.Tests/Visual/TestCaseIdleTracker.cs | 135 +++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 osu.Game.Tests/Visual/TestCaseIdleTracker.cs diff --git a/osu.Game.Tests/Visual/TestCaseIdleTracker.cs b/osu.Game.Tests/Visual/TestCaseIdleTracker.cs new file mode 100644 index 0000000000..33e2df9ff0 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseIdleTracker.cs @@ -0,0 +1,135 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Input; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + [TestFixture] + public class TestCaseIdleTracker : ManualInputManagerTestCase + { + private readonly IdleTrackingBox box1; + private readonly IdleTrackingBox box2; + private readonly IdleTrackingBox box3; + private readonly IdleTrackingBox box4; + + public TestCaseIdleTracker() + { + Children = new Drawable[] + { + box1 = new IdleTrackingBox(1000) + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Red, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }, + box2 = new IdleTrackingBox(2000) + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Green, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, + box3 = new IdleTrackingBox(3000) + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Blue, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + }, + box4 = new IdleTrackingBox(4000) + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Orange, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + }, + }; + } + + [Test] + public void TestNudge() + { + AddStep("move mouse to top left", () => InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.Centre)); + + AddUntilStep(() => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle, "Wait for all idle"); + + AddStep("nudge mouse", () => InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.Centre + new Vector2(1))); + + AddAssert("check not idle", () => !box1.IsIdle); + AddAssert("check idle", () => box2.IsIdle); + AddAssert("check idle", () => box3.IsIdle); + AddAssert("check idle", () => box4.IsIdle); + } + + [Test] + public void TestMovement() + { + AddStep("move mouse", () => InputManager.MoveMouseTo(box2.ScreenSpaceDrawQuad.Centre)); + + AddAssert("check not idle", () => box1.IsIdle); + AddAssert("check not idle", () => !box2.IsIdle); + AddAssert("check idle", () => box3.IsIdle); + AddAssert("check idle", () => box4.IsIdle); + + AddStep("move mouse", () => InputManager.MoveMouseTo(box3.ScreenSpaceDrawQuad.Centre)); + AddStep("move mouse", () => InputManager.MoveMouseTo(box4.ScreenSpaceDrawQuad.Centre)); + + AddAssert("check not idle", () => box1.IsIdle); + AddAssert("check not idle", () => !box2.IsIdle); + AddAssert("check idle", () => !box3.IsIdle); + AddAssert("check idle", () => !box4.IsIdle); + + AddUntilStep(() => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle, "Wait for all idle"); + } + + [Test] + public void TestTimings() + { + AddStep("move mouse", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre)); + + AddAssert("check not idle", () => !box1.IsIdle && !box2.IsIdle && !box3.IsIdle && !box4.IsIdle); + AddUntilStep(() => box1.IsIdle, "Wait for idle"); + AddAssert("check not idle", () => !box2.IsIdle && !box3.IsIdle && !box4.IsIdle); + AddUntilStep(() => box2.IsIdle, "Wait for idle"); + AddAssert("check not idle", () => !box3.IsIdle && !box4.IsIdle); + AddUntilStep(() => box3.IsIdle, "Wait for idle"); + + AddUntilStep(() => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle, "Wait for all idle"); + } + + private class IdleTrackingBox : CompositeDrawable + { + private readonly IdleTracker idleTracker; + + public bool IsIdle => idleTracker.IsIdle.Value; + + public IdleTrackingBox(double timeToIdle) + { + Box box; + + Alpha = 0.6f; + Scale = new Vector2(0.6f); + + InternalChildren = new Drawable[] + { + idleTracker = new IdleTracker(timeToIdle), + box = new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both, + }, + }; + + idleTracker.IsIdle.BindValueChanged(idle => box.Colour = idle ? Color4.White : Color4.Black, true); + } + } + } +}