diff --git a/osu-framework b/osu-framework index d29c8365ba..cd6f6e93c9 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit d29c8365ba3cf7924b57cf22341f4af55658764c +Subproject commit cd6f6e93c958e3e4e98db08dd7a443cabcf4742f diff --git a/osu-resources b/osu-resources index 92ec3d10b1..7bb0782200 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 92ec3d10b12c5e9bfc1d3b05d3db174a506efd6d +Subproject commit 7bb0782200abadf73b79ed1a3bc1d5b926c6a81e diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs new file mode 100644 index 0000000000..273422f2e9 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs @@ -0,0 +1,33 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics.Cursor; +using osu.Game.Rulesets.Osu.UI.Cursor; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseGameplayCursor : OsuTestCase, IProvideCursor + { + private GameplayCursor cursor; + + public override IReadOnlyList RequiredTypes => new [] { typeof(CursorTrail) }; + + public CursorContainer Cursor => cursor; + + public bool ProvidingUserCursor => true; + + [BackgroundDependencyLoader] + private void load() + { + Add(cursor = new GameplayCursor { RelativeSizeAxes = Axes.Both }); + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 37ca0c021b..dedfa28b7b 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -3,9 +3,9 @@ using System; using System.Diagnostics; +using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.Primitives; @@ -14,11 +14,12 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Timing; using OpenTK; +using OpenTK.Graphics; using OpenTK.Graphics.ES30; namespace osu.Game.Rulesets.Osu.UI.Cursor { - internal class CursorTrail : Drawable + internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition { private int currentIndex; @@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private float time; + public override bool IsPresent => true; + private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData(); private const int max_sprites = 2048; @@ -96,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor const int fade_clock_reset_threshold = 1000000; - time = (float)(Time.Current - timeOffset) / 500f; + time = (float)(Time.Current - timeOffset) / 300f; if (time > fade_clock_reset_threshold) resetTime(); } @@ -115,14 +118,16 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor protected override bool OnMouseMove(InputState state) { + Vector2 pos = state.Mouse.NativeState.Position; + if (lastPosition == null) { - lastPosition = state.Mouse.NativeState.Position; + lastPosition = pos; resampler.AddPosition(lastPosition.Value); return base.OnMouseMove(state); } - foreach (Vector2 pos2 in resampler.AddPosition(state.Mouse.NativeState.Position)) + foreach (Vector2 pos2 in resampler.AddPosition(pos)) { Trace.Assert(lastPosition.HasValue); @@ -162,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private class TrailDrawNodeSharedData { - public VertexBuffer VertexBuffer; + public VertexBuffer VertexBuffer; } private class TrailDrawNode : DrawNode @@ -188,7 +193,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor public override void Draw(Action vertexAction) { if (Shared.VertexBuffer == null) - Shared.VertexBuffer = new QuadVertexBuffer(max_sprites, BufferUsageHint.DynamicDraw); + Shared.VertexBuffer = new QuadVertexBuffer(max_sprites, BufferUsageHint.DynamicDraw); Shader.GetUniform("g_FadeClock").Value = Time; @@ -205,17 +210,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor int end = start; Vector2 pos = Parts[i].Position; - ColourInfo colour = DrawInfo.Colour; - colour.TopLeft.Linear.A = Parts[i].Time + colour.TopLeft.Linear.A; - colour.TopRight.Linear.A = Parts[i].Time + colour.TopRight.Linear.A; - colour.BottomLeft.Linear.A = Parts[i].Time + colour.BottomLeft.Linear.A; - colour.BottomRight.Linear.A = Parts[i].Time + colour.BottomRight.Linear.A; + float time = Parts[i].Time; Texture.DrawQuad( new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y), - colour, + DrawInfo.Colour, null, - v => Shared.VertexBuffer.Vertices[end++] = v); + v => Shared.VertexBuffer.Vertices[end++] = new TexturedTrailVertex + { + Position = v.Position, + TexturePosition = v.TexturePosition, + Time = time + 1, + Colour = v.Colour, + }); Parts[i].WasUpdated = false; } @@ -240,5 +247,26 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Shader.Unbind(); } } + + [StructLayout(LayoutKind.Sequential)] + public struct TexturedTrailVertex : IEquatable, IVertex + { + [VertexMember(2, VertexAttribPointerType.Float)] + public Vector2 Position; + [VertexMember(4, VertexAttribPointerType.Float)] + public Color4 Colour; + [VertexMember(2, VertexAttribPointerType.Float)] + public Vector2 TexturePosition; + [VertexMember(1, VertexAttribPointerType.Float)] + public float Time; + + public bool Equals(TexturedTrailVertex other) + { + return Position.Equals(other.Position) + && TexturePosition.Equals(other.TexturePosition) + && Colour.Equals(other.Colour) + && Time.Equals(other.Time); + } + } } } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index 0aeb14514d..34940a084a 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -20,13 +20,66 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { protected override Drawable CreateCursor() => new OsuCursor(); + protected override Container Content => fadeContainer; + + private readonly Container fadeContainer; + public GameplayCursor() { - Add(new CursorTrail { Depth = 1 }); + InternalChild = fadeContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new CursorTrail { Depth = 1 } + } + }; } private int downCount; + public bool OnPressed(OsuAction action) + { + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + downCount++; + ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad); + break; + } + + return false; + } + + public bool OnReleased(OsuAction action) + { + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + if (--downCount == 0) + ActiveCursor.ScaleTo(1, 200, Easing.OutQuad); + break; + } + + return false; + } + + public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input. + + protected override void PopIn() + { + fadeContainer.FadeTo(1, 300, Easing.OutQuint); + ActiveCursor.ScaleTo(1, 400, Easing.OutQuint); + } + + protected override void PopOut() + { + fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint); + ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint); + } + public class OsuCursor : Container { private Container cursorContainer; @@ -131,45 +184,5 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor cursorContainer.Scale = new Vector2(scale); } } - - public bool OnPressed(OsuAction action) - { - switch (action) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - downCount++; - ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad); - break; - } - - return false; - } - - public bool OnReleased(OsuAction action) - { - switch (action) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - if (--downCount == 0) - ActiveCursor.ScaleTo(1, 200, Easing.OutQuad); - break; - } - - 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/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 4a404e9526..8b7383b6b7 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -131,6 +131,7 @@ + diff --git a/osu.Game.Tests/Visual/TestCaseSkipButton.cs b/osu.Game.Tests/Visual/TestCaseSkipButton.cs index a4d2019cd7..df94d5147f 100644 --- a/osu.Game.Tests/Visual/TestCaseSkipButton.cs +++ b/osu.Game.Tests/Visual/TestCaseSkipButton.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual { base.LoadComplete(); - Add(new SkipButton(Clock.CurrentTime + 5000)); + Add(new SkipOverlay(Clock.CurrentTime + 5000)); } } } diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 615c124ea7..29b68abc21 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -18,7 +18,7 @@ using System.Collections.Generic; namespace osu.Game.Screens.Play { - public abstract class GameplayMenuOverlay : OverlayContainer, IRequireHighFrequencyMousePosition + public abstract class GameplayMenuOverlay : OverlayContainer { private const int transition_duration = 200; private const int button_height = 70; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c8ff261a93..8502812f26 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -164,7 +164,7 @@ namespace osu.Game.Screens.Play Alpha = 0, }, RulesetContainer, - new SkipButton(firstObjectTime) + new SkipOverlay(firstObjectTime) { Clock = Clock, // skip button doesn't want to use the audio clock directly ProcessCustomClock = false, diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipOverlay.cs similarity index 89% rename from osu.Game/Screens/Play/SkipButton.cs rename to osu.Game/Screens/Play/SkipOverlay.cs index 08bb26c72b..19ee0cb989 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -21,7 +21,7 @@ using osu.Game.Input.Bindings; namespace osu.Game.Screens.Play { - public class SkipButton : OverlayContainer, IKeyBindingHandler + public class SkipOverlay : OverlayContainer, IKeyBindingHandler { private readonly double startTime; @@ -35,8 +35,9 @@ namespace osu.Game.Screens.Play private double displayTime; public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + protected override bool BlockPassThroughMouse => false; - public SkipButton(double startTime) + public SkipOverlay(double startTime) { this.startTime = startTime; @@ -51,12 +52,6 @@ namespace osu.Game.Screens.Play Origin = Anchor.Centre; } - protected override bool OnMouseMove(InputState state) - { - fadeContainer.State = Visibility.Visible; - return base.OnMouseMove(state); - } - [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -121,15 +116,9 @@ namespace osu.Game.Screens.Play Expire(); } - protected override void PopIn() - { - this.FadeIn(); - } + protected override void PopIn() => this.FadeIn(); - protected override void PopOut() - { - this.FadeOut(); - } + protected override void PopOut() => this.FadeOut(); protected override void Update() { @@ -137,6 +126,13 @@ namespace osu.Game.Screens.Play remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, Easing.OutQuint); } + protected override bool OnMouseMove(InputState state) + { + if (!state.Mouse.HasAnyButtonPressed) + fadeContainer.State = Visibility.Visible; + return base.OnMouseMove(state); + } + public bool OnPressed(GlobalAction action) { switch (action) @@ -176,7 +172,7 @@ namespace osu.Game.Screens.Play if (stateChanged) this.FadeIn(500, Easing.OutExpo); - if (!IsHovered) + if (!IsHovered && !IsDragged) using (BeginDelayedSequence(1000)) scheduledHide = Schedule(() => State = Visibility.Hidden); break; @@ -194,6 +190,18 @@ namespace osu.Game.Screens.Play base.LoadComplete(); State = Visibility.Visible; } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + scheduledHide?.Cancel(); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + State = Visibility.Visible; + return base.OnMouseUp(state, args); + } } private class Button : OsuClickableContainer @@ -274,7 +282,7 @@ namespace osu.Game.Screens.Play flow.TransformSpacingTo(new Vector2(5), 500, Easing.OutQuint); box.FadeColour(colourHover, 500, Easing.OutQuint); background.FadeTo(0.4f, 500, Easing.OutQuint); - return base.OnHover(state); + return true; } protected override void OnHoverLost(InputState state) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 86833a6d19..337a36ba08 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -809,7 +809,7 @@ - +