From 79bfe7880af8243b4d5c7c83d81236c872a17314 Mon Sep 17 00:00:00 2001 From: Chirag Mahesh Date: Tue, 25 Nov 2025 06:56:18 +0000 Subject: [PATCH 1/8] Move LocalSpacePosition calculation until the time of render Would address #35734 --- .../UI/Cursor/CursorTrail.cs | 34 ++++---- .../TestSceneGameplayCursorSizeChange.cs | 85 +++++++++++++++++++ 2 files changed, 102 insertions(+), 17 deletions(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneGameplayCursorSizeChange.cs diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 1c2d69fa00..8a45475f0f 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -164,20 +164,18 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor return base.OnMouseMove(e); } - protected void AddTrail(Vector2 position) + protected void AddTrail(Vector2 screenSpacePosition) { - position = ToLocalSpace(position); - if (InterpolateMovements) { if (!lastPosition.HasValue) { - lastPosition = position; + lastPosition = screenSpacePosition; resampler.AddPosition(lastPosition.Value); return; } - foreach (Vector2 pos2 in resampler.AddPosition(position)) + foreach (Vector2 pos2 in resampler.AddPosition(screenSpacePosition)) { Trace.Assert(lastPosition.HasValue); @@ -198,14 +196,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor } else { - lastPosition = position; + lastPosition = screenSpacePosition; addPart(lastPosition.Value); } } - private void addPart(Vector2 localSpacePosition) + private void addPart(Vector2 screenSpacePosition) { - parts[currentIndex].Position = localSpacePosition; + parts[currentIndex].ScreenSpacePosition = screenSpacePosition; parts[currentIndex].Time = time + 1; parts[currentIndex].Scale = NewPartScale; ++parts[currentIndex].InvalidationID; @@ -217,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private struct TrailPart { - public Vector2 Position; + public Vector2 ScreenSpacePosition; public float Time; public Vector2 Scale; public long InvalidationID; @@ -304,11 +302,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor if (time - part.Time >= 1) continue; + Vector2 localSpacePosition = Vector2Extensions.Transform(part.ScreenSpacePosition, DrawInfo.MatrixInverse); + vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X, part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y), - part.Position, sin, cos), + new Vector2(localSpacePosition.X - texture.DisplayWidth * originPosition.X * part.Scale.X, localSpacePosition.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y), + localSpacePosition, sin, cos), TexturePosition = textureRect.BottomLeft, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.BottomLeft.Linear, @@ -318,8 +318,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, - part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y), part.Position, sin, cos), + new Vector2(localSpacePosition.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, + localSpacePosition.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y), localSpacePosition, sin, cos), TexturePosition = textureRect.BottomRight, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.BottomRight.Linear, @@ -329,8 +329,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y), - part.Position, sin, cos), + new Vector2(localSpacePosition.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, localSpacePosition.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y), + localSpacePosition, sin, cos), TexturePosition = textureRect.TopRight, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.TopRight.Linear, @@ -340,8 +340,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X, part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y), - part.Position, sin, cos), + new Vector2(localSpacePosition.X - texture.DisplayWidth * originPosition.X * part.Scale.X, localSpacePosition.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y), + localSpacePosition, sin, cos), TexturePosition = textureRect.TopLeft, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.TopLeft.Linear, diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayCursorSizeChange.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayCursorSizeChange.cs new file mode 100644 index 0000000000..22b3a8e378 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayCursorSizeChange.cs @@ -0,0 +1,85 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Screens; +using osu.Framework.Testing; +using osu.Game.Configuration; +using osu.Game.Rulesets; +using osu.Game.Skinning; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public partial class TestSceneGameplayCursorSizeChange : OsuPlayerTestScene + { + [Resolved] + private SkinManager? skins { get; set; } + + protected new PausePlayer Player => (PausePlayer)base.Player; + + [BackgroundDependencyLoader] + private void load() + { + if (skins != null) skins.CurrentSkinInfo.Value = skins.DefaultClassicSkin.SkinInfo; + } + + [SetUpSteps] + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("resume player", () => Player.GameplayClockContainer.Start()); + AddUntilStep("clock running", () => Player.GameplayClockContainer.IsRunning); + } + + [Test] + public void TestChangeCursorSize() + { + AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre)); + AddStep("move cursor to top left", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft)); + AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre)); + AddStep("move cursor to top right", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopRight)); + AddStep("press escape", () => InputManager.Key(Key.Escape)); + + for (float cursorSize = 0.4f; cursorSize <= 1.6f + 0.001f; cursorSize += 0.4f) + { + AddWaitStep("wait 2 seconds", 2); + float newCursorSize = cursorSize; + AddStep($"gameplay cursor size: {newCursorSize:F1}", () => LocalConfig.SetValue(OsuSetting.GameplayCursorSize, newCursorSize)); + } + } + + protected override TestPlayer CreatePlayer(Ruleset ruleset) => new PausePlayer(); + + protected partial class PausePlayer : TestPlayer + { + public double LastPauseTime { get; private set; } + public double LastResumeTime { get; private set; } + + public override void OnEntering(ScreenTransitionEvent e) + { + base.OnEntering(e); + GameplayClockContainer.Stop(); + } + + private bool? isRunning; + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + if (GameplayClockContainer.IsRunning != isRunning) + { + isRunning = GameplayClockContainer.IsRunning; + + if (isRunning.Value) + LastResumeTime = GameplayClockContainer.CurrentTime; + else + LastPauseTime = GameplayClockContainer.CurrentTime; + } + } + } + } +} From 9e2ea63e7092e8563ea8d7e0542d0535cbf7a2e9 Mon Sep 17 00:00:00 2001 From: Chirag Mahesh Date: Thu, 27 Nov 2025 14:52:57 +0000 Subject: [PATCH 2/8] Revert Changes to Trail Position Calculation - Revert changes to CursorTrail.cs made during 79bfe7880af8243b4d5c7c83d81236c872a17314 --- .../UI/Cursor/CursorTrail.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 8a45475f0f..1c2d69fa00 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -164,18 +164,20 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor return base.OnMouseMove(e); } - protected void AddTrail(Vector2 screenSpacePosition) + protected void AddTrail(Vector2 position) { + position = ToLocalSpace(position); + if (InterpolateMovements) { if (!lastPosition.HasValue) { - lastPosition = screenSpacePosition; + lastPosition = position; resampler.AddPosition(lastPosition.Value); return; } - foreach (Vector2 pos2 in resampler.AddPosition(screenSpacePosition)) + foreach (Vector2 pos2 in resampler.AddPosition(position)) { Trace.Assert(lastPosition.HasValue); @@ -196,14 +198,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor } else { - lastPosition = screenSpacePosition; + lastPosition = position; addPart(lastPosition.Value); } } - private void addPart(Vector2 screenSpacePosition) + private void addPart(Vector2 localSpacePosition) { - parts[currentIndex].ScreenSpacePosition = screenSpacePosition; + parts[currentIndex].Position = localSpacePosition; parts[currentIndex].Time = time + 1; parts[currentIndex].Scale = NewPartScale; ++parts[currentIndex].InvalidationID; @@ -215,7 +217,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private struct TrailPart { - public Vector2 ScreenSpacePosition; + public Vector2 Position; public float Time; public Vector2 Scale; public long InvalidationID; @@ -302,13 +304,11 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor if (time - part.Time >= 1) continue; - Vector2 localSpacePosition = Vector2Extensions.Transform(part.ScreenSpacePosition, DrawInfo.MatrixInverse); - vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(localSpacePosition.X - texture.DisplayWidth * originPosition.X * part.Scale.X, localSpacePosition.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y), - localSpacePosition, sin, cos), + new Vector2(part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X, part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y), + part.Position, sin, cos), TexturePosition = textureRect.BottomLeft, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.BottomLeft.Linear, @@ -318,8 +318,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(localSpacePosition.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, - localSpacePosition.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y), localSpacePosition, sin, cos), + new Vector2(part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, + part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y), part.Position, sin, cos), TexturePosition = textureRect.BottomRight, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.BottomRight.Linear, @@ -329,8 +329,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(localSpacePosition.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, localSpacePosition.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y), - localSpacePosition, sin, cos), + new Vector2(part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y), + part.Position, sin, cos), TexturePosition = textureRect.TopRight, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.TopRight.Linear, @@ -340,8 +340,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(localSpacePosition.X - texture.DisplayWidth * originPosition.X * part.Scale.X, localSpacePosition.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y), - localSpacePosition, sin, cos), + new Vector2(part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X, part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y), + part.Position, sin, cos), TexturePosition = textureRect.TopLeft, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.TopLeft.Linear, From ae33690632821391593315d52a868d571920d335 Mon Sep 17 00:00:00 2001 From: Chirag Mahesh Date: Thu, 27 Nov 2025 14:59:08 +0000 Subject: [PATCH 3/8] Implement CursorTrail Scaling - Add CursorScale property to CursorTrail and adjust for scaling --- .../UI/Cursor/CursorTrail.cs | 32 ++++++++++++++++--- .../UI/Cursor/OsuCursorContainer.cs | 2 +- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 1c2d69fa00..fc5ad2d955 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -49,6 +49,18 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor /// protected bool AllowPartRotation { get; set; } + private Vector2 cursorScale; + + public Vector2 CursorScale + { + get => cursorScale; + set + { + cursorScale = value; + Invalidate(Invalidation.DrawNode); + } + } + /// /// The trail part texture origin. /// @@ -233,6 +245,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private float time; private float fadeExponent; private float angle; + private Vector2 cursorScale; private readonly TrailPart[] parts = new TrailPart[max_sprites]; private Vector2 originPosition; @@ -253,6 +266,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor time = Source.time; fadeExponent = Source.FadeExponent; angle = Source.AllowPartRotation ? float.DegreesToRadians(Source.PartRotation) : 0; + cursorScale = Source.cursorScale; originPosition = Vector2.Zero; @@ -307,7 +321,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X, part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y), + new Vector2( + part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X * cursorScale.X, + part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y * cursorScale.Y), part.Position, sin, cos), TexturePosition = textureRect.BottomLeft, TextureRect = new Vector4(0, 0, 1, 1), @@ -318,8 +334,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, - part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y), part.Position, sin, cos), + new Vector2( + part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X * cursorScale.X, + part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y * cursorScale.Y), + part.Position, sin, cos), TexturePosition = textureRect.BottomRight, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.BottomRight.Linear, @@ -329,7 +347,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y), + new Vector2( + part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X * cursorScale.X, + part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y * cursorScale.Y), part.Position, sin, cos), TexturePosition = textureRect.TopRight, TextureRect = new Vector4(0, 0, 1, 1), @@ -340,7 +360,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { Position = rotateAround( - new Vector2(part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X, part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y), + new Vector2( + part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X * cursorScale.X, + part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y * cursorScale.Y), part.Position, sin, cos), TexturePosition = textureRect.TopLeft, TextureRect = new Vector4(0, 0, 1, 1), diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 974d99d7c8..bf0ed528fb 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor var newScale = new Vector2(e.NewValue); rippleVisualiser.CursorScale = newScale; - cursorTrail.Scale = newScale; + if (cursorTrail.Drawable is CursorTrail trail) trail.CursorScale = newScale; }, true); } From d8d7c808328f31a306dc0136c3e148a9c2c6e078 Mon Sep 17 00:00:00 2001 From: Chirag Mahesh Date: Thu, 27 Nov 2025 16:08:11 +0000 Subject: [PATCH 4/8] Persist cursorScale on skin refresh --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index bf0ed528fb..0044cf26dd 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -31,6 +31,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private readonly SkinnableDrawable cursorTrail; + private Vector2 cursorScale; + private readonly CursorRippleVisualiser rippleVisualiser; public OsuCursorContainer() @@ -61,10 +63,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor ActiveCursor.CursorScale.BindValueChanged(e => { - var newScale = new Vector2(e.NewValue); + cursorScale = new Vector2(e.NewValue); - rippleVisualiser.CursorScale = newScale; - if (cursorTrail.Drawable is CursorTrail trail) trail.CursorScale = newScale; + rippleVisualiser.CursorScale = cursorScale; + if (cursorTrail.Drawable is CursorTrail trail) trail.CursorScale = cursorScale; }, true); } @@ -86,6 +88,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { trail.NewPartScale = ActiveCursor.CurrentExpandedScale; trail.PartRotation = ActiveCursor.CurrentRotation; + trail.CursorScale = cursorScale; } } From 78c6973298f038cf1609f9e48ee90ff648d5b931 Mon Sep 17 00:00:00 2001 From: Chirag Mahesh Date: Thu, 27 Nov 2025 16:38:19 +0000 Subject: [PATCH 5/8] move cursorScale persistance into OsuCursor and use skinnableCursorScale --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 2 ++ osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs | 10 ++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index e84fb9e2d6..37ceb296b8 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -36,6 +36,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor /// public Vector2 CurrentExpandedScale => skinnableCursor.ExpandTarget?.Scale ?? Vector2.One; + public Vector2 CurrentCursorScale => skinnableCursor.Scale; + /// /// The current rotation of the cursor. /// diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index 0044cf26dd..df72f8be97 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -31,8 +31,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private readonly SkinnableDrawable cursorTrail; - private Vector2 cursorScale; - private readonly CursorRippleVisualiser rippleVisualiser; public OsuCursorContainer() @@ -63,10 +61,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor ActiveCursor.CursorScale.BindValueChanged(e => { - cursorScale = new Vector2(e.NewValue); + var newScale = new Vector2(e.NewValue); - rippleVisualiser.CursorScale = cursorScale; - if (cursorTrail.Drawable is CursorTrail trail) trail.CursorScale = cursorScale; + rippleVisualiser.CursorScale = newScale; + if (cursorTrail.Drawable is CursorTrail trail) trail.CursorScale = newScale; }, true); } @@ -88,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { trail.NewPartScale = ActiveCursor.CurrentExpandedScale; trail.PartRotation = ActiveCursor.CurrentRotation; - trail.CursorScale = cursorScale; + trail.CursorScale = ActiveCursor.CurrentCursorScale; } } From d1d76a76ba19eaa8ea1aac6b4a0bebae294f6079 Mon Sep 17 00:00:00 2001 From: Chirag Mahesh Date: Thu, 4 Dec 2025 16:40:58 +0000 Subject: [PATCH 6/8] Refactor trail scale update logic into a dedicated method --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 2 -- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs | 9 +++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index 37ceb296b8..e84fb9e2d6 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -36,8 +36,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor /// public Vector2 CurrentExpandedScale => skinnableCursor.ExpandTarget?.Scale ?? Vector2.One; - public Vector2 CurrentCursorScale => skinnableCursor.Scale; - /// /// The current rotation of the cursor. /// diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs index df72f8be97..e04382d194 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs @@ -64,8 +64,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor var newScale = new Vector2(e.NewValue); rippleVisualiser.CursorScale = newScale; - if (cursorTrail.Drawable is CursorTrail trail) trail.CursorScale = newScale; + updateTrailScale(); }, true); + cursorTrail.OnSkinChanged += updateTrailScale; + } + + private void updateTrailScale() + { + if (cursorTrail.Drawable is CursorTrail trail) trail.CursorScale = new Vector2(ActiveCursor.CursorScale.Value); } private int downCount; @@ -86,7 +92,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { trail.NewPartScale = ActiveCursor.CurrentExpandedScale; trail.PartRotation = ActiveCursor.CurrentRotation; - trail.CursorScale = ActiveCursor.CurrentCursorScale; } } From 107098314a486ce8959020dd822223eab5ed5094 Mon Sep 17 00:00:00 2001 From: Chirag Mahesh Date: Fri, 5 Dec 2025 17:22:10 +0000 Subject: [PATCH 7/8] Move and refactor TestSceneGameplayCursorSizeChange into Ruleset Osu Tests --- .../TestSceneGameplayCursorSizeChange.cs | 52 ++++++++++++ .../TestSceneGameplayCursorSizeChange.cs | 85 ------------------- 2 files changed, 52 insertions(+), 85 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursorSizeChange.cs delete mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneGameplayCursorSizeChange.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursorSizeChange.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursorSizeChange.cs new file mode 100644 index 0000000000..27a83887dd --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursorSizeChange.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Configuration; +using osu.Game.Skinning; +using osu.Game.Tests.Visual; +using osuTK.Input; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public partial class TestSceneGameplayCursorSizeChange : PlayerTestScene + { + private const float initial_cursor_size = 1f; + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + + [Resolved] + private SkinManager? skins { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + if (skins != null) skins.CurrentSkinInfo.Value = skins.DefaultClassicSkin.SkinInfo; + } + + [SetUpSteps] + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep($"Set gameplay cursor size: 1", () => LocalConfig.SetValue(OsuSetting.GameplayCursorSize, initial_cursor_size)); + AddStep("resume player", () => Player.GameplayClockContainer.Start()); + AddUntilStep("clock running", () => Player.GameplayClockContainer.IsRunning); + } + + [Test] + public void TestPausedChangeCursorSize() + { + AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre)); + AddStep("move cursor to top left", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft)); + AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre)); + AddStep("move cursor to top right", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopRight)); + AddStep("press escape", () => InputManager.Key(Key.Escape)); + + AddSliderStep("cursor size", 0.1f, 2f, 1f, v => LocalConfig.SetValue(OsuSetting.GameplayCursorSize, v)); + } + + protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(true, false); + } +} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayCursorSizeChange.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayCursorSizeChange.cs deleted file mode 100644 index 22b3a8e378..0000000000 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayCursorSizeChange.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Screens; -using osu.Framework.Testing; -using osu.Game.Configuration; -using osu.Game.Rulesets; -using osu.Game.Skinning; -using osuTK.Input; - -namespace osu.Game.Tests.Visual.Gameplay -{ - public partial class TestSceneGameplayCursorSizeChange : OsuPlayerTestScene - { - [Resolved] - private SkinManager? skins { get; set; } - - protected new PausePlayer Player => (PausePlayer)base.Player; - - [BackgroundDependencyLoader] - private void load() - { - if (skins != null) skins.CurrentSkinInfo.Value = skins.DefaultClassicSkin.SkinInfo; - } - - [SetUpSteps] - public override void SetUpSteps() - { - base.SetUpSteps(); - - AddStep("resume player", () => Player.GameplayClockContainer.Start()); - AddUntilStep("clock running", () => Player.GameplayClockContainer.IsRunning); - } - - [Test] - public void TestChangeCursorSize() - { - AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre)); - AddStep("move cursor to top left", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft)); - AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre)); - AddStep("move cursor to top right", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopRight)); - AddStep("press escape", () => InputManager.Key(Key.Escape)); - - for (float cursorSize = 0.4f; cursorSize <= 1.6f + 0.001f; cursorSize += 0.4f) - { - AddWaitStep("wait 2 seconds", 2); - float newCursorSize = cursorSize; - AddStep($"gameplay cursor size: {newCursorSize:F1}", () => LocalConfig.SetValue(OsuSetting.GameplayCursorSize, newCursorSize)); - } - } - - protected override TestPlayer CreatePlayer(Ruleset ruleset) => new PausePlayer(); - - protected partial class PausePlayer : TestPlayer - { - public double LastPauseTime { get; private set; } - public double LastResumeTime { get; private set; } - - public override void OnEntering(ScreenTransitionEvent e) - { - base.OnEntering(e); - GameplayClockContainer.Stop(); - } - - private bool? isRunning; - - protected override void UpdateAfterChildren() - { - base.UpdateAfterChildren(); - - if (GameplayClockContainer.IsRunning != isRunning) - { - isRunning = GameplayClockContainer.IsRunning; - - if (isRunning.Value) - LastResumeTime = GameplayClockContainer.CurrentTime; - else - LastPauseTime = GameplayClockContainer.CurrentTime; - } - } - } - } -} From a6c001244ff44e140cfd1f5006fd370f4e2a20bd Mon Sep 17 00:00:00 2001 From: Chirag Mahesh Date: Sat, 6 Dec 2025 11:18:18 +0000 Subject: [PATCH 8/8] Redundant string interpolation --- .../TestSceneGameplayCursorSizeChange.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursorSizeChange.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursorSizeChange.cs index 27a83887dd..c94e575032 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursorSizeChange.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursorSizeChange.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Tests { base.SetUpSteps(); - AddStep($"Set gameplay cursor size: 1", () => LocalConfig.SetValue(OsuSetting.GameplayCursorSize, initial_cursor_size)); + AddStep("Set gameplay cursor size: 1", () => LocalConfig.SetValue(OsuSetting.GameplayCursorSize, initial_cursor_size)); AddStep("resume player", () => Player.GameplayClockContainer.Start()); AddUntilStep("clock running", () => Player.GameplayClockContainer.IsRunning); }