diff --git a/osu-framework b/osu-framework index 49b563e2cf..8f36ddab94 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 49b563e2cf170eb19006b98dd5b69c2398362d9e +Subproject commit 8f36ddab946ff538620081ede7719461d4732b79 diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 8e6391168b..f37282366a 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -45,7 +45,7 @@ namespace osu.Desktop { protected override string LocateBasePath() { - Func checkExists = p => Directory.Exists(Path.Combine(p, "Songs")); + bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")); string stableInstallPath; diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 923356c610..090173f38d 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -31,7 +31,8 @@ namespace osu.Desktop.Overlays private OsuConfigManager config; private OsuGameBase game; - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; [BackgroundDependencyLoader] private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config) diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 8ea30a2899..7f56c3bbb1 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -58,6 +58,7 @@ namespace osu.Game.Rulesets.Catch.UI public override void Add(DrawableHitObject h) { h.Depth = (float)h.HitObject.StartTime; + h.OnJudgement += onJudgement; base.Add(h); @@ -65,6 +66,6 @@ namespace osu.Game.Rulesets.Catch.UI fruit.CheckPosition = CheckIfWeCanCatch; } - public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement); + private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement); } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index d15303af39..1d739c114e 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -321,7 +321,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy break; } - Func isDoubleSample = sample => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH; + bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH; bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0; canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample); diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModKeyCoop.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModKeyCoop.cs index 382eea589c..82036d1e82 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModKeyCoop.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModKeyCoop.cs @@ -12,7 +12,7 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModKeyCoop : Mod, IKeyBindingMod, IApplicableToBeatmapConverter, IApplicableToRulesetContainer + public class ManiaModKeyCoop : Mod, IApplicableToBeatmapConverter, IApplicableToRulesetContainer { public override string Name => "KeyCoop"; public override string ShortenedName => "2P"; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 58f024870d..57a4888b2b 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables holdStartTime = null; // If the key has been released too early, the user should not receive full score for the release - if (!tail.AllJudged) + if (!tail.IsHit) hasBroken = true; return true; diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index f2eb2ef326..ccbb5797db 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -205,12 +205,13 @@ namespace osu.Game.Rulesets.Mania.UI public override void Add(DrawableHitObject hitObject) { hitObject.Depth = (float)hitObject.HitObject.StartTime; - hitObject.AccentColour = AccentColour; + hitObject.OnJudgement += onJudgement; + HitObjects.Add(hitObject); } - public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) { if (!judgement.IsHit) return; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index ba4909615d..101586f3e4 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Configuration; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; @@ -80,5 +81,10 @@ namespace osu.Game.Rulesets.Mania.UI return null; } + + internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + { + getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement); + } } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs index 2d9e7157da..15de119342 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs @@ -186,15 +186,16 @@ namespace osu.Game.Rulesets.Mania.UI var maniaObject = (ManiaHitObject)h.HitObject; int columnIndex = maniaObject.Column - firstColumnIndex; Columns.ElementAt(columnIndex).Add(h); + h.OnJudgement += OnJudgement; } public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline)); - public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { var maniaObject = (ManiaHitObject)judgedObject.HitObject; int columnIndex = maniaObject.Column - firstColumnIndex; - Columns[columnIndex].OnJudgement(judgedObject, judgement); + //Columns[columnIndex].OnJudgement(judgedObject, judgement); judgements.Clear(); judgements.Add(new DrawableManiaJudgement(judgement) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs index a3f0b79475..5f232b1889 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs @@ -1,15 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Graphics.Cursor; using osu.Game.Rulesets.Osu.UI; namespace osu.Game.Rulesets.Osu.Edit { public class OsuEditPlayfield : OsuPlayfield { - protected override CursorContainer CreateCursor() => null; - protected override bool ProxyApproachCircles => false; } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs index 96b9acbf78..56efc25fa5 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.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.Graphics.Cursor; using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; @@ -15,5 +16,7 @@ namespace osu.Game.Rulesets.Osu.Edit } protected override Playfield CreatePlayfield() => new OsuEditPlayfield(); + + protected override CursorContainer CreateCursor() => null; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs index 7c96a2a8cf..c44d7594ad 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs @@ -11,7 +11,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class SpinnerBackground : CircularContainer, IHasAccentColour { - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; protected Box Disc; diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index 325f22f425..0aeb14514d 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -159,5 +159,17 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor return false; } + + protected override void PopIn() + { + ActiveCursor.FadeTo(1, 250, Easing.OutQuint); + ActiveCursor.ScaleTo(1, 400, Easing.OutQuint); + } + + protected override void PopOut() + { + ActiveCursor.FadeTo(0, 250, Easing.OutQuint); + ActiveCursor.ScaleTo(0.6f, 250, Easing.In); + } } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index e7d47da391..17521f8992 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -12,8 +12,6 @@ using osu.Game.Rulesets.UI; using System.Linq; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; -using osu.Game.Rulesets.Osu.UI.Cursor; -using osu.Framework.Graphics.Cursor; namespace osu.Game.Rulesets.Osu.UI { @@ -23,8 +21,6 @@ namespace osu.Game.Rulesets.Osu.UI private readonly Container judgementLayer; private readonly ConnectionRenderer connectionLayer; - public override bool ProvidingUserCursor => true; - // Todo: This should not be a thing, but is currently required for the editor // https://github.com/ppy/osu-framework/issues/1283 protected virtual bool ProxyApproachCircles => true; @@ -70,19 +66,12 @@ namespace osu.Game.Rulesets.Osu.UI }); } - protected override void LoadComplete() - { - base.LoadComplete(); - - var cursor = CreateCursor(); - if (cursor != null) - AddInternal(cursor); - } - public override void Add(DrawableHitObject h) { h.Depth = (float)h.HitObject.StartTime; + h.OnJudgement += onJudgement; + var c = h as IDrawableHitObjectWithProxiedApproach; if (c != null && ProxyApproachCircles) approachCircles.Add(c.ProxiedLayer.CreateProxy()); @@ -97,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.UI .OrderBy(h => h.StartTime).OfType(); } - public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) { var osuJudgement = (OsuJudgement)judgement; var osuObject = (OsuHitObject)judgedObject.HitObject; @@ -113,7 +102,5 @@ namespace osu.Game.Rulesets.Osu.UI judgementLayer.Add(explosion); } - - protected virtual CursorContainer CreateCursor() => new GameplayCursor(); } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index 5c7413a71e..526348062f 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.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.Graphics.Cursor; using osu.Framework.Input; using OpenTK; using osu.Game.Beatmaps; @@ -10,6 +11,7 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Replays; @@ -49,5 +51,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay); protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f); + + protected override CursorContainer CreateCursor() => new GameplayCursor(); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index 2fa6c8ed95..29d464f614 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (timeOffset < 0) return; - int countHit = NestedHitObjects.Count(o => o.AllJudged); + int countHit = NestedHitObjects.Count(o => o.IsHit); if (countHit > HitObject.RequiredGoodHits) { diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs index 9500a1a747..fd396c201d 100644 --- a/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/Tests/TestCaseTaikoPlayfield.cs @@ -143,18 +143,18 @@ namespace osu.Game.Rulesets.Taiko.Tests var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; - rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult }); + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult }); if (RNG.Next(10) == 0) { - rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult }); - rulesetContainer.Playfield.OnJudgement(h, new TaikoStrongHitJudgement()); + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult }); + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoStrongHitJudgement()); } } private void addMissJudgement() { - rulesetContainer.Playfield.OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss }); + ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss }); } private void addBarLine(bool major, double delay = scroll_time) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 3c5093d82f..49c87f7480 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -209,6 +209,8 @@ namespace osu.Game.Rulesets.Taiko.UI { h.Depth = (float)h.HitObject.StartTime; + h.OnJudgement += OnJudgement; + base.Add(h); var barline = h as DrawableBarLine; @@ -221,7 +223,7 @@ namespace osu.Game.Rulesets.Taiko.UI swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy()); } - public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) + internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null) { diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 322f0ec608..ece1f626ec 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -117,8 +117,8 @@ namespace osu.Game.Tests.Beatmaps.IO //ensure we were stored to beatmap database backing... Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1)."); - Func> queryBeatmaps = () => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0); - Func> queryBeatmapSets = () => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526); + IEnumerable queryBeatmaps() => store.QueryBeatmaps(s => s.BeatmapSet.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0); + IEnumerable queryBeatmapSets() => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526); //if we don't re-check here, the set will be inserted but the beatmaps won't be present yet. waitForOrAssert(() => queryBeatmaps().Count() == 12, diff --git a/osu.Game.Tests/Visual/TestCaseCursors.cs b/osu.Game.Tests/Visual/TestCaseCursors.cs new file mode 100644 index 0000000000..363f6b53f0 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseCursors.cs @@ -0,0 +1,274 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Framework.Testing.Input; +using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseCursors : OsuTestCase + { + private readonly ManualInputManager inputManager; + private readonly CursorOverrideContainer cursorOverrideContainer; + private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6]; + + public TestCaseCursors() + { + Child = inputManager = new ManualInputManager + { + Child = cursorOverrideContainer = new CursorOverrideContainer + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + // Middle user + cursorBoxes[0] = new CustomCursorBox(Color4.Green) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.5f), + }, + // Top-left user + cursorBoxes[1] = new CustomCursorBox(Color4.Blue) + { + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Bottom-right user + cursorBoxes[2] = new CustomCursorBox(Color4.Red) + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Bottom-left local + cursorBoxes[3] = new CustomCursorBox(Color4.Magenta, false) + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Top-right local + cursorBoxes[4] = new CustomCursorBox(Color4.Cyan, false) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.4f) + }, + // Left-local + cursorBoxes[5] = new CustomCursorBox(Color4.Yellow, false) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.2f, 1), + }, + } + } + }; + + returnUserInput(); + + AddToggleStep("Smooth transitions", b => cursorBoxes.ForEach(box => box.SmoothTransition = b)); + + testUserCursor(); + testLocalCursor(); + testUserCursorOverride(); + testMultipleLocalCursors(); + returnUserInput(); + } + + /// + /// Returns input back to the user. + /// + private void returnUserInput() + { + AddStep("Return user input", () => inputManager.UseParentState = true); + } + + /// + /// -- Green Box -- + /// Tests whether hovering in and out of a drawable that provides the user cursor (green) + /// results in the correct visibility state for that cursor. + /// + private void testUserCursor() + { + AddStep("Move to green area", () => inputManager.MoveMouseTo(cursorBoxes[0])); + AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); + } + + /// + /// -- Purple Box -- + /// Tests whether hovering in and out of a drawable that provides a local cursor (purple) + /// results in the correct visibility and state for that cursor. + /// + private void testLocalCursor() + { + AddStep("Move to purple area", () => inputManager.MoveMouseTo(cursorBoxes[3])); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); + AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); + AddAssert("Check global cursor at mouse", () => checkAtMouse(cursorOverrideContainer.Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); + } + + /// + /// -- Blue-Green Box Boundary -- + /// Tests whether overriding a user cursor (green) with another user cursor (blue) + /// results in the correct visibility and states for the cursors. + /// + private void testUserCursorOverride() + { + AddStep("Move to blue-green boundary", () => inputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); + AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); + AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check blue cursor not visible", () => !checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check green cursor not visible", () => !checkVisible(cursorBoxes[0].Cursor)); + } + + /// + /// -- Yellow-Purple Box Boundary -- + /// Tests whether multiple local cursors (purple + yellow) may be visible and at the mouse position at the same time. + /// + private void testMultipleLocalCursors() + { + AddStep("Move to yellow-purple boundary", () => inputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + } + + /// + /// -- Yellow-Blue Box Boundary -- + /// Tests whether a local cursor (yellow) may be displayed along with a user cursor override (blue). + /// + private void testUserOverrideWithLocal() + { + AddStep("Move to yellow-blue boundary", () => inputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); + AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); + AddStep("Move out", moveOut); + AddAssert("Check blue cursor invisible", () => !checkVisible(cursorBoxes[1].Cursor)); + AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); + } + + /// + /// Moves the cursor to a point not covered by any cursor containers. + /// + private void moveOut() + => inputManager.MoveMouseTo(new Vector2(inputManager.ScreenSpaceDrawQuad.Centre.X, inputManager.ScreenSpaceDrawQuad.TopLeft.Y)); + + /// + /// Checks if a cursor is visible. + /// + /// The cursor to check. + private bool checkVisible(CursorContainer cursorContainer) => cursorContainer.State == Visibility.Visible; + + /// + /// Checks if a cursor is at the current inputmanager screen position. + /// + /// The cursor to check. + private bool checkAtMouse(CursorContainer cursorContainer) + => Precision.AlmostEquals(inputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition)); + + private class CustomCursorBox : Container, IProvideCursor + { + public bool SmoothTransition; + + public CursorContainer Cursor { get; } + public bool ProvidingUserCursor { get; } + + public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => base.ReceiveMouseInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor; + + private readonly Box background; + + public CustomCursorBox(Color4 cursorColour, bool providesUserCursor = true) + { + ProvidingUserCursor = providesUserCursor; + + Colour = cursorColour; + Masking = true; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.1f + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = providesUserCursor ? "User cursor" : "Local cursor" + }, + Cursor = new TestCursorContainer + { + State = providesUserCursor ? Visibility.Hidden : Visibility.Visible, + } + }; + } + + protected override bool OnHover(InputState state) + { + background.FadeTo(0.4f, 250, Easing.OutQuint); + return false; + } + + protected override void OnHoverLost(InputState state) + { + background.FadeTo(0.1f, 250); + base.OnHoverLost(state); + } + } + + private class TestCursorContainer : CursorContainer + { + protected override Drawable CreateCursor() => new TestCursor(); + + private class TestCursor : CircularContainer + { + public TestCursor() + { + Origin = Anchor.Centre; + + Size = new Vector2(50); + Masking = true; + + Blending = BlendingMode.Additive; + Alpha = 0.5f; + + Child = new Box { RelativeSizeAxes = Axes.Both }; + } + } + } + } +} diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index f16cf73039..809de2b8db 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual // this is by no means clean. should be replacing inside of OsuGameBase somehow. var context = new OsuDbContext(); - Func contextFactory = () => context; + OsuDbContext contextFactory() => context; dependencies.Cache(rulesets = new RulesetStore(contextFactory)); dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null) diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 2eb79f6b35..76b426bf5d 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -104,6 +104,7 @@ + diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 5e23389b89..6f9d83473f 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -30,7 +30,9 @@ namespace osu.Game.Graphics.Backgrounds /// private const float edge_smoothness = 1; - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; + public Color4 ColourLight = Color4.White; public Color4 ColourDark = Color4.Black; diff --git a/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs new file mode 100644 index 0000000000..0fae4579fa --- /dev/null +++ b/osu.Game/Graphics/Cursor/CursorOverrideContainer.cs @@ -0,0 +1,67 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Input; + +namespace osu.Game.Graphics.Cursor +{ + /// + /// A container which provides a which can be overridden by hovered s. + /// + public class CursorOverrideContainer : Container, IProvideCursor + { + protected override Container Content => content; + private readonly Container content; + + /// + /// Whether any cursors can be displayed. + /// + public bool CanShowCursor = true; + + public CursorContainer Cursor { get; } + public bool ProvidingUserCursor => true; + + public CursorOverrideContainer() + { + AddRangeInternal(new Drawable[] + { + Cursor = new MenuCursor { State = Visibility.Hidden }, + content = new Container { RelativeSizeAxes = Axes.Both } + }); + } + + private InputManager inputManager; + + protected override void LoadComplete() + { + base.LoadComplete(); + inputManager = GetContainingInputManager(); + } + + private IProvideCursor currentTarget; + protected override void Update() + { + base.Update(); + + if (!CanShowCursor) + { + currentTarget?.Cursor?.Hide(); + return; + } + + var newTarget = inputManager.HoveredDrawables.OfType().FirstOrDefault(t => t.ProvidingUserCursor) ?? this; + + if (currentTarget == newTarget) + return; + + currentTarget?.Cursor?.Hide(); + newTarget.Cursor?.Show(); + + currentTarget = newTarget; + } + } +} diff --git a/osu.Game/Graphics/Cursor/IProvideCursor.cs b/osu.Game/Graphics/Cursor/IProvideCursor.cs new file mode 100644 index 0000000000..91b44234fb --- /dev/null +++ b/osu.Game/Graphics/Cursor/IProvideCursor.cs @@ -0,0 +1,26 @@ +// 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.Graphics.Cursor; + +namespace osu.Game.Graphics.Cursor +{ + /// + /// Interface for s that display cursors which can replace the user's cursor. + /// + public interface IProvideCursor : IDrawable + { + /// + /// The cursor provided by this . + /// May be null if no cursor should be visible. + /// + CursorContainer Cursor { get; } + + /// + /// Whether should be displayed as the singular user cursor. This will temporarily hide any other user cursor. + /// This value is checked every frame and may be used to control whether multiple cursors are displayed (e.g. watching replays). + /// + bool ProvidingUserCursor { get; } + } +} diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index 39af99f02e..0de6279b2e 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -99,8 +99,8 @@ namespace osu.Game.Graphics.Cursor protected override void PopOut() { - ActiveCursor.FadeTo(0, 900, Easing.OutQuint); - ActiveCursor.ScaleTo(0, 500, Easing.In); + ActiveCursor.FadeTo(0, 250, Easing.OutQuint); + ActiveCursor.ScaleTo(0.6f, 250, Easing.In); } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs index 99afc02d5d..5ee0aba9cf 100644 --- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs +++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs @@ -42,7 +42,9 @@ namespace osu.Game.Graphics.UserInterface //don't allow clicking between transitions and don't make the chevron clickable public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceiveMouseInputAt(screenSpacePos); - public override bool HandleInput => State == Visibility.Visible; + + public override bool HandleKeyboardInput => State == Visibility.Visible; + public override bool HandleMouseInput => State == Visibility.Visible; private Visibility state; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5604ef8f3c..b48e25f1fe 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -157,6 +157,11 @@ namespace osu.Game { base.LoadComplete(); + // The next time this is updated is in UpdateAfterChildren, which occurs too late and results + // in the cursor being shown for a few frames during the intro. + // This prevents the cursor from showing until we have a screen with CursorVisible = true + CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; + // hook up notifications to components. BeatmapManager.PostNotification = n => notifications?.Post(n); BeatmapManager.GetStableStorage = GetStorageForStableInstall; @@ -297,8 +302,6 @@ namespace osu.Game else Toolbar.State = Visibility.Visible; }; - - Cursor.State = Visibility.Hidden; } private void forwardLoggedErrorsToNotifications() @@ -447,7 +450,7 @@ namespace osu.Game mainContent.Padding = new MarginPadding { Top = ToolbarOffset }; - Cursor.State = currentScreen?.HasLocalCursorDisplayed == false ? Visibility.Visible : Visibility.Hidden; + CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; } private void screenAdded(Screen newScreen) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 1982fa0db5..ef02f1a7ec 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -44,6 +44,8 @@ namespace osu.Game protected KeyBindingStore KeyBindingStore; + protected CursorOverrideContainer CursorOverrideContainer; + protected override string MainResourceFile => @"osu.Game.Resources.dll"; public APIAccess API; @@ -52,8 +54,6 @@ namespace osu.Game protected override Container Content => content; - protected MenuCursor Cursor; - public Bindable Beatmap { get; private set; } private Bindable fpsDisplayVisible; @@ -211,21 +211,14 @@ namespace osu.Game GlobalKeyBindingInputManager globalBinding; - base.Content.Add(new DrawSizePreservingFillContainer + CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; + CursorOverrideContainer.Child = globalBinding = new GlobalKeyBindingInputManager(this) { - Children = new Drawable[] - { - Cursor = new MenuCursor(), - globalBinding = new GlobalKeyBindingInputManager(this) - { - RelativeSizeAxes = Axes.Both, - Child = content = new OsuTooltipContainer(Cursor) - { - RelativeSizeAxes = Axes.Both, - } - } - } - }); + RelativeSizeAxes = Axes.Both, + Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both } + }; + + base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer }); KeyBindingStore.Register(globalBinding); dependencies.Cache(globalBinding); diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index 6a1bd8e182..65803b477b 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -22,7 +22,8 @@ namespace osu.Game.Overlays { private readonly Container box; - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; private readonly SpriteText textLine1; private readonly SpriteText textLine2; @@ -121,7 +122,7 @@ namespace osu.Game.Overlays trackSetting(frameworkConfig.GetBindable(FrameworkSetting.AudioDevice), v => display(v, "Audio Device", string.IsNullOrEmpty(v) ? "Default" : v, v)); trackSetting(frameworkConfig.GetBindable(FrameworkSetting.ShowLogOverlay), v => display(v, "Debug Logs", v ? "visible" : "hidden", "Ctrl+F10")); - Action displayResolution = delegate { display(null, "Screen Resolution", frameworkConfig.Get(FrameworkSetting.Width) + "x" + frameworkConfig.Get(FrameworkSetting.Height)); }; + void displayResolution() => display(null, "Screen Resolution", frameworkConfig.Get(FrameworkSetting.Width) + "x" + frameworkConfig.Get(FrameworkSetting.Height)); trackSetting(frameworkConfig.GetBindable(FrameworkSetting.Width), v => displayResolution()); trackSetting(frameworkConfig.GetBindable(FrameworkSetting.Height), v => displayResolution()); diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 6a9f4b84b0..924b5d6c9d 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -319,11 +319,11 @@ namespace osu.Game.Overlays.Profile colourBar.Show(); } - Action boldItalic = t => + void boldItalic(SpriteText t) { t.Font = @"Exo2.0-BoldItalic"; t.Alpha = 1; - }; + } if (user.Age != null) { @@ -474,7 +474,8 @@ namespace osu.Game.Overlays.Profile private class LinkFlowContainer : OsuTextFlowContainer { - public override bool HandleInput => true; + public override bool HandleKeyboardInput => true; + public override bool HandleMouseInput => true; public LinkFlowContainer(Action defaultCreationParameters = null) : base(defaultCreationParameters) { @@ -488,7 +489,8 @@ namespace osu.Game.Overlays.Profile { private readonly OsuHoverContainer content; - public override bool HandleInput => content.Action != null; + public override bool HandleKeyboardInput => content.Action != null; + public override bool HandleMouseInput => content.Action != null; protected override Container Content => content ?? (Container)this; diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index 0fa6fa23a2..6d58c78c37 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -87,7 +87,8 @@ namespace osu.Game.Overlays.Toolbar ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(); } - public override bool HandleInput => !ruleset.Disabled; + public override bool HandleKeyboardInput => !ruleset.Disabled; + public override bool HandleMouseInput => !ruleset.Disabled; private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); diff --git a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs index c840cb0c38..fcb2f8f57f 100644 --- a/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs +++ b/osu.Game/Rulesets/Edit/Layers/Selection/HitObjectSelectionBox.cs @@ -165,7 +165,8 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection } private bool isActive = true; - public override bool HandleInput => isActive; + public override bool HandleKeyboardInput => isActive; + public override bool HandleMouseInput => isActive; public override void Hide() { diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 0f6642ae0e..c1bf55b214 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -40,7 +40,8 @@ namespace osu.Game.Rulesets.Judgements Anchor = Anchor.Centre, Text = judgement.Result.GetDescription().ToUpper(), Font = @"Venera", - TextSize = 16 + Scale = new Vector2(0.85f, 1), + TextSize = 12 } }; } diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index 97b2fe7d1e..c4c0f38faf 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods { const float ratio = 1.4f; difficulty.CircleSize *= 1.3f; // CS uses a custom 1.3 ratio. - difficulty.ApproachRate *= ratio; + difficulty.ApproachRate = Math.Min(difficulty.ApproachRate * ratio, 10.0f); difficulty.DrainRate *= ratio; difficulty.OverallDifficulty *= ratio; } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index af14c43a3f..8680ff4e83 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -68,7 +68,8 @@ namespace osu.Game.Rulesets.Objects.Drawables private bool judgementFinalized => judgements.LastOrDefault()?.Final == true; public bool Interactive = true; - public override bool HandleInput => Interactive; + public override bool HandleKeyboardInput => Interactive; + public override bool HandleMouseInput => Interactive; public override bool RemoveWhenNotAlive => false; public override bool RemoveCompletedTransforms => false; @@ -167,6 +168,11 @@ namespace osu.Game.Rulesets.Objects.Drawables { if (nestedHitObjects == null) nestedHitObjects = new List(); + + h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j); + h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j); + h.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j); + nestedHitObjects.Add(h); } diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 2e2fb91b12..25da00362e 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -1,11 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; -using osu.Game.Rulesets.Judgements; using osu.Framework.Allocation; namespace osu.Game.Rulesets.UI @@ -19,14 +20,16 @@ namespace osu.Game.Rulesets.UI public Container ScaledContent; - /// - /// Whether we are currently providing the local user a gameplay cursor. - /// - public virtual bool ProvidingUserCursor => false; - protected override Container Content => content; private readonly Container content; + private List nestedPlayfields; + + /// + /// All the s nested inside this playfield. + /// + public IReadOnlyList NestedPlayfields => nestedPlayfields; + /// /// A container for keeping track of DrawableHitObjects. /// @@ -61,7 +64,7 @@ namespace osu.Game.Rulesets.UI /// /// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield. /// - public virtual void PostProcess() { } + public virtual void PostProcess() => nestedPlayfields?.ForEach(p => p.PostProcess()); /// /// Adds a DrawableHitObject to this Playfield. @@ -76,11 +79,17 @@ namespace osu.Game.Rulesets.UI public virtual void Remove(DrawableHitObject h) => HitObjects.Remove(h); /// - /// Triggered when a new occurs on a . + /// Registers a as a nested . + /// This does not add the to the draw hierarchy. /// - /// The object that occured for. - /// The that occurred. - public virtual void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { } + /// The to add. + protected void AddNested(Playfield otherPlayfield) + { + if (nestedPlayfields == null) + nestedPlayfields = new List(); + + nestedPlayfields.Add(otherPlayfield); + } /// /// Creates the container that will be used to contain the s. diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 4d559549b7..626b56ad67 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -13,6 +13,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using osu.Framework.Graphics.Cursor; using osu.Framework.Input; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; @@ -43,11 +44,6 @@ namespace osu.Game.Rulesets.UI /// public PassThroughInputManager KeyBindingInputManager; - /// - /// Whether we are currently providing the local user a gameplay cursor. - /// - public virtual bool ProvidingUserCursor => false; - /// /// Whether we have a replay loaded currently. /// @@ -61,6 +57,11 @@ namespace osu.Game.Rulesets.UI /// public Playfield Playfield => playfield.Value; + /// + /// The cursor provided by this . May be null if no cursor is provided. + /// + public readonly CursorContainer Cursor; + protected readonly Ruleset Ruleset; /// @@ -71,6 +72,8 @@ namespace osu.Game.Rulesets.UI { Ruleset = ruleset; playfield = new Lazy(CreatePlayfield); + + Cursor = CreateCursor(); } public abstract ScoreProcessor CreateScoreProcessor(); @@ -98,6 +101,12 @@ namespace osu.Game.Rulesets.UI ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; } + + /// + /// Creates the cursor. May be null if the doesn't provide a custom cursor. + /// + protected virtual CursorContainer CreateCursor() => null; + /// /// Creates a Playfield. /// @@ -144,8 +153,6 @@ namespace osu.Game.Rulesets.UI /// protected readonly bool IsForCurrentRuleset; - public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor; - public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this); protected override Container Content => content; @@ -212,6 +219,9 @@ namespace osu.Game.Rulesets.UI AddInternal(KeyBindingInputManager); KeyBindingInputManager.Add(Playfield); + if (Cursor != null) + KeyBindingInputManager.Add(Cursor); + loadObjects(); } @@ -252,12 +262,7 @@ namespace osu.Game.Rulesets.UI if (drawableObject == null) continue; - drawableObject.OnJudgement += (d, j) => - { - Playfield.OnJudgement(d, j); - OnJudgement?.Invoke(j); - }; - + drawableObject.OnJudgement += (d, j) => OnJudgement?.Invoke(j); drawableObject.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(j); Playfield.Add(drawableObject); diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 11185015b8..287e917c7b 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -76,25 +75,6 @@ namespace osu.Game.Rulesets.UI.Scrolling HitObjects.TimeRange.BindTo(VisibleTimeRange); } - private List nestedPlayfields; - /// - /// All the s nested inside this playfield. - /// - public IEnumerable NestedPlayfields => nestedPlayfields; - - /// - /// Adds a to this playfield. The nested - /// will be given all of the same speed adjustments as this playfield. - /// - /// The to add. - protected void AddNested(ScrollingPlayfield otherPlayfield) - { - if (nestedPlayfields == null) - nestedPlayfields = new List(); - - nestedPlayfields.Add(otherPlayfield); - } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { if (!UserScrollSpeedAdjustment) diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs index 286545270f..5f6b6801ce 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.UI.Scrolling private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield) { playfield.HitObjects.AddControlPoint(controlPoint); - playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p)); + playfield.NestedPlayfields?.OfType().ForEach(p => applySpeedAdjustment(controlPoint, p)); } /// diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index f5dd5cb500..268be51347 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -222,7 +222,8 @@ namespace osu.Game.Screens.Menu boxHoverLayer.FadeOut(800, Easing.OutExpo); } - public override bool HandleInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; + public override bool HandleKeyboardInput => state != ButtonState.Exploded; + public override bool HandleMouseInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; protected override void Update() { diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 529565312e..72c26af2a6 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -199,7 +199,8 @@ namespace osu.Game.Screens.Menu private MenuState state; - public override bool HandleInput => state != MenuState.Exit; + public override bool HandleKeyboardInput => state != MenuState.Exit; + public override bool HandleMouseInput => state != MenuState.Exit; public MenuState State { diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 977c8828d2..8285416ecb 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -19,8 +19,7 @@ namespace osu.Game.Screens.Menu private Color4 iconColour; public override bool ShowOverlaysOnEnter => false; - - public override bool HasLocalCursorDisplayed => true; + public override bool CursorVisible => false; public Disclaimer() { diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index fcee071f04..10b08d704d 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -31,9 +31,8 @@ namespace osu.Game.Screens.Menu private SampleChannel welcome; private SampleChannel seeya; - public override bool HasLocalCursorDisplayed => true; - public override bool ShowOverlaysOnEnter => false; + public override bool CursorVisible => false; protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty(); diff --git a/osu.Game/Screens/Menu/IntroSequence.cs b/osu.Game/Screens/Menu/IntroSequence.cs index 577eb33d18..ff3b4eba56 100644 --- a/osu.Game/Screens/Menu/IntroSequence.cs +++ b/osu.Game/Screens/Menu/IntroSequence.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Linq; using OpenTK; using OpenTK.Graphics; @@ -188,7 +187,7 @@ namespace osu.Game.Screens.Menu mediumRing.ResizeTo(130, 340, Easing.OutQuad); mediumRing.Foreground.ResizeTo(1, 880, Easing.Out); - Func remainingTime = () => length - TransformDelay; + double remainingTime() => length - TransformDelay; using (BeginDelayedSequence(250, true)) { diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 3fb5cc6d0d..3a3f3d4650 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -64,7 +64,8 @@ namespace osu.Game.Screens.Menu private readonly float[] frequencyAmplitudes = new float[256]; - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; private Shader shader; private readonly Texture texture; diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs index 3cf2ce8e89..f94e2bddc1 100644 --- a/osu.Game/Screens/Menu/MenuSideFlashes.cs +++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs @@ -19,7 +19,8 @@ namespace osu.Game.Screens.Menu { public class MenuSideFlashes : BeatSyncedContainer { - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; private readonly Bindable beatmap = new Bindable(); diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 4a13ae421e..b91ff0d74b 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -231,7 +231,7 @@ namespace osu.Game.Screens.Menu /// If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared. public void AppendAnimatingAction(Action action, bool waitForPrevious) { - Action runnableAction = () => + void runnableAction() { if (waitForPrevious) this.DelayUntilTransformsFinished().Schedule(action); @@ -240,12 +240,12 @@ namespace osu.Game.Screens.Menu ClearTransforms(); action(); } - }; + } if (IsLoaded) runnableAction(); else - Schedule(() => runnableAction()); + Schedule(runnableAction); } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 0111dceb40..a2d41dc206 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -35,9 +35,12 @@ namespace osu.Game.Screens /// public virtual bool ShowOverlaysOnEnter => true; - protected new OsuGameBase Game => base.Game as OsuGameBase; + /// + /// Whether this allows the cursor to be displayed. + /// + public virtual bool CursorVisible => true; - public virtual bool HasLocalCursorDisplayed => false; + protected new OsuGameBase Game => base.Game as OsuGameBase; private OsuLogo logo; diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index af222b82e2..615c124ea7 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -142,8 +142,6 @@ namespace osu.Game.Screens.Play } } - 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); diff --git a/osu.Game/Screens/Play/KeyCounterCollection.cs b/osu.Game/Screens/Play/KeyCounterCollection.cs index 554fbbaf61..e40776ae4a 100644 --- a/osu.Game/Screens/Play/KeyCounterCollection.cs +++ b/osu.Game/Screens/Play/KeyCounterCollection.cs @@ -111,7 +111,8 @@ namespace osu.Game.Screens.Play } } - public override bool HandleInput => receptor == null; + public override bool HandleKeyboardInput => receptor == null; + public override bool HandleMouseInput => receptor == null; private Receptor receptor; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index fc8b0f2c88..31d9fac2ad 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -23,22 +23,22 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; using osu.Framework.Audio.Sample; +using osu.Framework.Graphics.Cursor; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Graphics.Cursor; using osu.Game.Online.API; using osu.Game.Screens.Play.BreaksOverlay; using osu.Game.Storyboards.Drawables; namespace osu.Game.Screens.Play { - public class Player : OsuScreen + public class Player : OsuScreen, IProvideCursor { protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap); public override bool ShowOverlaysOnEnter => false; - public override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && RulesetContainer.ProvidingUserCursor; - public Action RestartRequested; public override bool AllowBeatmapRulesetChange => false; @@ -51,6 +51,9 @@ namespace osu.Game.Screens.Play public int RestartCount; + public CursorContainer Cursor => RulesetContainer.Cursor; + public bool ProvidingUserCursor => RulesetContainer?.Cursor != null && !RulesetContainer.HasReplayLoaded; + private IAdjustableClock adjustableSourceClock; private FramedOffsetClock offsetClock; private DecoupleableInterpolatingFramedClock decoupledClock; @@ -176,13 +179,13 @@ namespace osu.Game.Screens.Play }, Children = new Drawable[] { - new SkipButton(firstObjectTime) { AudioClock = decoupledClock }, new Container { RelativeSizeAxes = Axes.Both, Clock = offsetClock, Child = RulesetContainer, }, + new SkipButton(firstObjectTime) { AudioClock = decoupledClock }, hudOverlay = new HUDOverlay { Anchor = Anchor.Centre, @@ -194,7 +197,7 @@ namespace osu.Game.Screens.Play Origin = Anchor.Centre, Clock = decoupledClock, Breaks = beatmap.Breaks - }, + } } }, failOverlay = new FailOverlay diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index 55b2e21c53..827d77a73a 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -21,7 +21,7 @@ using osu.Game.Input.Bindings; namespace osu.Game.Screens.Play { - public class SkipButton : Container, IKeyBindingHandler + public class SkipButton : OverlayContainer, IKeyBindingHandler { private readonly double startTime; public IAdjustableClock AudioClock; @@ -36,6 +36,8 @@ namespace osu.Game.Screens.Play { this.startTime = startTime; + State = Visibility.Visible; + RelativePositionAxes = Axes.Both; RelativeSizeAxes = Axes.Both; @@ -112,6 +114,16 @@ namespace osu.Game.Screens.Play Expire(); } + protected override void PopIn() + { + this.FadeIn(); + } + + protected override void PopOut() + { + this.FadeOut(); + } + protected override void Update() { base.Update(); diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 897fe4bba7..8aad741ad1 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -28,7 +28,8 @@ namespace osu.Game.Screens.Play public Action OnSeek; - public override bool HandleInput => AllowSeeking; + public override bool HandleKeyboardInput => AllowSeeking; + public override bool HandleMouseInput => AllowSeeking; private IClock audioClock; public IClock AudioClock { set { audioClock = info.AudioClock = value; } } diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 7bccad0c34..48013f7943 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -21,7 +21,8 @@ namespace osu.Game.Screens.Play public int ColumnCount => columns.Length; - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; private int progress; public int Progress diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index c0767cc07a..6a6042d7d4 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -51,7 +51,8 @@ namespace osu.Game.Screens.Select /// public Action SelectionChanged; - public override bool HandleInput => AllowSelection; + public override bool HandleKeyboardInput => AllowSelection; + public override bool HandleMouseInput => AllowSelection; /// /// Used to avoid firing null selections before the initial beatmaps have been loaded via . diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 7431a6e0e6..357931b878 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -266,7 +266,7 @@ namespace osu.Game.Screens.Select /// private void carouselSelectionChanged(BeatmapInfo beatmap) { - Action performLoad = delegate + void performLoad() { // We may be arriving here due to another component changing the bindable Beatmap. // In these cases, the other component has already loaded the beatmap, so we don't need to do so again. @@ -279,7 +279,7 @@ namespace osu.Game.Screens.Select } UpdateBeatmap(Beatmap.Value); - }; + } if (beatmap?.Equals(beatmapNoDebounce) == true) return; diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index 17a678c191..498dd7de6f 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -265,7 +265,7 @@ namespace osu.Game.Screens.Tournament private void writeResults(string text) { - Action writeAction = () => + void writeAction() { try { @@ -280,9 +280,9 @@ namespace osu.Game.Screens.Tournament { Logger.Error(ex, "Failed to write results."); } - }; + } - writeOp = writeOp?.ContinueWith(t => { writeAction(); }) ?? Task.Run(writeAction); + writeOp = writeOp?.ContinueWith(t => { writeAction(); }) ?? Task.Run((Action)writeAction); } private void reloadTeams() diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index 7e8a80d86a..2489369493 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -26,7 +26,8 @@ namespace osu.Game.Storyboards.Drawables protected override Container Content => content; protected override Vector2 DrawScale => new Vector2(Parent.DrawHeight / 480); - public override bool HandleInput => false; + public override bool HandleKeyboardInput => false; + public override bool HandleMouseInput => false; private bool passing = true; public bool Passing diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 57926ec647..85da54e317 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -388,8 +388,10 @@ + +