1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-13 15:03:13 +08:00

Merge branch 'master' into beatmap-update-test

This commit is contained in:
Dean Herbert 2022-07-26 17:27:36 +09:00 committed by GitHub
commit 7f75184a01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 298 additions and 569 deletions

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Utils; using osu.Framework.Utils;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Utils; using osu.Framework.Utils;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Replays;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Linq; using System.Linq;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods

View File

@ -1,9 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
@ -37,9 +36,9 @@ namespace osu.Game.Rulesets.Catch.Mods
public override float DefaultFlashlightSize => 350; public override float DefaultFlashlightSize => 350;
protected override Flashlight CreateFlashlight() => new CatchFlashlight(this, playfield); protected override Flashlight CreateFlashlight() => new CatchFlashlight(this, playfield.AsNonNull());
private CatchPlayfield playfield; private CatchPlayfield? playfield;
public override void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset) public override void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
{ {

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Beatmaps;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Linq; using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Linq; using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Beatmaps;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System; using System;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods

View File

@ -1,8 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable using System.Diagnostics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
@ -20,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Mods
{ {
public override string Description => @"Use the mouse to control the catcher."; public override string Description => @"Use the mouse to control the catcher.";
private DrawableRuleset<CatchHitObject> drawableRuleset; private DrawableRuleset<CatchHitObject>? drawableRuleset;
public void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset) public void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
{ {
@ -29,6 +28,8 @@ namespace osu.Game.Rulesets.Catch.Mods
public void ApplyToPlayer(Player player) public void ApplyToPlayer(Player player)
{ {
Debug.Assert(drawableRuleset != null);
if (!drawableRuleset.HasReplayLoaded.Value) if (!drawableRuleset.HasReplayLoaded.Value)
drawableRuleset.Cursor.Add(new MouseInputHelper((CatchPlayfield)drawableRuleset.Playfield)); drawableRuleset.Cursor.Add(new MouseInputHelper((CatchPlayfield)drawableRuleset.Playfield));
} }

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods

View File

@ -142,7 +142,6 @@ namespace osu.Game.Tests.Database
{ {
Task.Run(async () => Task.Run(async () =>
{ {
// ReSharper disable once AccessToDisposedClosure
var beatmapSet = await importer.Import(new ImportTask(TestResources.GetTestBeatmapStream(), "renatus.osz")); var beatmapSet = await importer.Import(new ImportTask(TestResources.GetTestBeatmapStream(), "renatus.osz"));
Assert.NotNull(beatmapSet); Assert.NotNull(beatmapSet);
@ -311,6 +310,7 @@ namespace osu.Game.Tests.Database
} }
finally finally
{ {
File.Delete(temp);
Directory.Delete(extractedFolder, true); Directory.Delete(extractedFolder, true);
} }
}); });

View File

@ -32,31 +32,29 @@ namespace osu.Game.Tests.Database
[Test] [Test]
public void TestAccessAfterStorageMigrate() public void TestAccessAfterStorageMigrate()
{ {
RunTestWithRealm((realm, storage) => using (var migratedStorage = new TemporaryNativeStorage("realm-test-migration-target"))
{ {
var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata()); RunTestWithRealm((realm, storage) =>
Live<BeatmapInfo>? liveBeatmap = null;
realm.Run(r =>
{ {
r.Write(_ => r.Add(beatmap)); var beatmap = new BeatmapInfo(CreateRuleset(), new BeatmapDifficulty(), new BeatmapMetadata());
liveBeatmap = beatmap.ToLive(realm); Live<BeatmapInfo>? liveBeatmap = null;
});
realm.Run(r =>
{
r.Write(_ => r.Add(beatmap));
liveBeatmap = beatmap.ToLive(realm);
});
using (var migratedStorage = new TemporaryNativeStorage("realm-test-migration-target"))
{
migratedStorage.DeleteDirectory(string.Empty); migratedStorage.DeleteDirectory(string.Empty);
using (realm.BlockAllOperations("testing")) using (realm.BlockAllOperations("testing"))
{
storage.Migrate(migratedStorage); storage.Migrate(migratedStorage);
}
Assert.IsFalse(liveBeatmap?.PerformRead(l => l.Hidden)); Assert.IsFalse(liveBeatmap?.PerformRead(l => l.Hidden));
} });
}); }
} }
[Test] [Test]
@ -341,14 +339,12 @@ namespace osu.Game.Tests.Database
liveBeatmap.PerformRead(resolved => liveBeatmap.PerformRead(resolved =>
{ {
// retrieval causes an implicit refresh. even changes that aren't related to the retrieval are fired at this point. // retrieval causes an implicit refresh. even changes that aren't related to the retrieval are fired at this point.
// ReSharper disable once AccessToDisposedClosure
Assert.AreEqual(2, outerRealm.All<BeatmapInfo>().Count()); Assert.AreEqual(2, outerRealm.All<BeatmapInfo>().Count());
Assert.AreEqual(1, changesTriggered); Assert.AreEqual(1, changesTriggered);
// can access properties without a crash. // can access properties without a crash.
Assert.IsFalse(resolved.Hidden); Assert.IsFalse(resolved.Hidden);
// ReSharper disable once AccessToDisposedClosure
outerRealm.Write(r => outerRealm.Write(r =>
{ {
// can use with the main context. // can use with the main context.

View File

@ -4,11 +4,11 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.IO; using osu.Game.IO;
@ -20,22 +20,15 @@ namespace osu.Game.Tests.Database
[TestFixture] [TestFixture]
public abstract class RealmTest public abstract class RealmTest
{ {
private static readonly TemporaryNativeStorage storage; protected void RunTestWithRealm([InstantHandle] Action<RealmAccess, OsuStorage> testAction, [CallerMemberName] string caller = "")
static RealmTest()
{
storage = new TemporaryNativeStorage("realm-test");
storage.DeleteDirectory(string.Empty);
}
protected void RunTestWithRealm(Action<RealmAccess, OsuStorage> testAction, [CallerMemberName] string caller = "")
{ {
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(callingMethodName: caller)) using (HeadlessGameHost host = new CleanRunHeadlessGameHost(callingMethodName: caller))
{ {
host.Run(new RealmTestGame(() => host.Run(new RealmTestGame(() =>
{ {
// ReSharper disable once AccessToDisposedClosure var defaultStorage = host.Storage;
var testStorage = new OsuStorage(host, storage.GetStorageForDirectory(caller));
var testStorage = new OsuStorage(host, defaultStorage);
using (var realm = new RealmAccess(testStorage, OsuGameBase.CLIENT_DATABASE_FILENAME)) using (var realm = new RealmAccess(testStorage, OsuGameBase.CLIENT_DATABASE_FILENAME))
{ {
@ -58,7 +51,7 @@ namespace osu.Game.Tests.Database
{ {
host.Run(new RealmTestGame(async () => host.Run(new RealmTestGame(async () =>
{ {
var testStorage = storage.GetStorageForDirectory(caller); var testStorage = host.Storage;
using (var realm = new RealmAccess(testStorage, OsuGameBase.CLIENT_DATABASE_FILENAME)) using (var realm = new RealmAccess(testStorage, OsuGameBase.CLIENT_DATABASE_FILENAME))
{ {
@ -116,7 +109,7 @@ namespace osu.Game.Tests.Database
private class RealmTestGame : Framework.Game private class RealmTestGame : Framework.Game
{ {
public RealmTestGame(Func<Task> work) public RealmTestGame([InstantHandle] Func<Task> work)
{ {
// ReSharper disable once AsyncVoidLambda // ReSharper disable once AsyncVoidLambda
Scheduler.Add(async () => Scheduler.Add(async () =>
@ -126,7 +119,7 @@ namespace osu.Game.Tests.Database
}); });
} }
public RealmTestGame(Action work) public RealmTestGame([InstantHandle] Action work)
{ {
Scheduler.Add(() => Scheduler.Add(() =>
{ {

View File

@ -12,7 +12,6 @@ using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Solo;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
@ -110,30 +109,30 @@ namespace osu.Game.Tests.Online
} }
[Test] [Test]
public void TestDeserialiseSubmittableScoreWithEmptyMods() public void TestDeserialiseSoloScoreWithEmptyMods()
{ {
var score = new SubmittableScore(new ScoreInfo var score = SoloScoreInfo.ForSubmission(new ScoreInfo
{ {
User = new APIUser(), User = new APIUser(),
Ruleset = new OsuRuleset().RulesetInfo, Ruleset = new OsuRuleset().RulesetInfo,
}); });
var deserialised = JsonConvert.DeserializeObject<SubmittableScore>(JsonConvert.SerializeObject(score)); var deserialised = JsonConvert.DeserializeObject<SoloScoreInfo>(JsonConvert.SerializeObject(score));
Assert.That(deserialised?.Mods.Length, Is.Zero); Assert.That(deserialised?.Mods.Length, Is.Zero);
} }
[Test] [Test]
public void TestDeserialiseSubmittableScoreWithCustomModSetting() public void TestDeserialiseSoloScoreWithCustomModSetting()
{ {
var score = new SubmittableScore(new ScoreInfo var score = SoloScoreInfo.ForSubmission(new ScoreInfo
{ {
Mods = new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 2 } } }, Mods = new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 2 } } },
User = new APIUser(), User = new APIUser(),
Ruleset = new OsuRuleset().RulesetInfo, Ruleset = new OsuRuleset().RulesetInfo,
}); });
var deserialised = JsonConvert.DeserializeObject<SubmittableScore>(JsonConvert.SerializeObject(score)); var deserialised = JsonConvert.DeserializeObject<SoloScoreInfo>(JsonConvert.SerializeObject(score));
Assert.That((deserialised?.Mods[0])?.Settings["speed_change"], Is.EqualTo(2)); Assert.That((deserialised?.Mods[0])?.Settings["speed_change"], Is.EqualTo(2));
} }

View File

@ -6,7 +6,7 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.IO.Serialization; using osu.Game.IO.Serialization;
using osu.Game.Online.Solo; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
namespace osu.Game.Tests.Online namespace osu.Game.Tests.Online
@ -15,12 +15,12 @@ namespace osu.Game.Tests.Online
/// Basic testing to ensure our attribute-based naming is correctly working. /// Basic testing to ensure our attribute-based naming is correctly working.
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class TestSubmittableScoreJsonSerialization public class TestSoloScoreInfoJsonSerialization
{ {
[Test] [Test]
public void TestScoreSerialisationViaExtensionMethod() public void TestScoreSerialisationViaExtensionMethod()
{ {
var score = new SubmittableScore(TestResources.CreateTestScoreInfo()); var score = SoloScoreInfo.ForSubmission(TestResources.CreateTestScoreInfo());
string serialised = score.Serialize(); string serialised = score.Serialize();
@ -31,7 +31,7 @@ namespace osu.Game.Tests.Online
[Test] [Test]
public void TestScoreSerialisationWithoutSettings() public void TestScoreSerialisationWithoutSettings()
{ {
var score = new SubmittableScore(TestResources.CreateTestScoreInfo()); var score = SoloScoreInfo.ForSubmission(TestResources.CreateTestScoreInfo());
string serialised = JsonConvert.SerializeObject(score); string serialised = JsonConvert.SerializeObject(score);

View File

@ -8,26 +8,17 @@ using osu.Framework.Graphics;
namespace osu.Game.Tests.Visual.Editing namespace osu.Game.Tests.Visual.Editing
{ {
[Ignore("Timeline initialisation is kinda broken.")] // Initial work to rectify this was done in https://github.com/ppy/osu/pull/19297, but needs more massaging to work.
public class TestSceneTimelineZoom : TimelineTestScene public class TestSceneTimelineZoom : TimelineTestScene
{ {
public override Drawable CreateTestComponent() => Empty(); public override Drawable CreateTestComponent() => Empty();
[Test] [Test]
[FlakyTest]
/*
* Fail rate around 0.3%
*
* TearDown : osu.Framework.Testing.Drawables.Steps.AssertButton+TracedException : range halved
* --TearDown
* at osu.Framework.Threading.ScheduledDelegate.RunTaskInternal()
* at osu.Framework.Threading.Scheduler.Update()
* at osu.Framework.Graphics.Drawable.UpdateSubTree()
*/
public void TestVisibleRangeUpdatesOnZoomChange() public void TestVisibleRangeUpdatesOnZoomChange()
{ {
double initialVisibleRange = 0; double initialVisibleRange = 0;
AddUntilStep("wait for load", () => MusicController.TrackLoaded);
AddStep("reset zoom", () => TimelineArea.Timeline.Zoom = 100); AddStep("reset zoom", () => TimelineArea.Timeline.Zoom = 100);
AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange); AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange);
@ -45,6 +36,8 @@ namespace osu.Game.Tests.Visual.Editing
{ {
double initialVisibleRange = 0; double initialVisibleRange = 0;
AddUntilStep("wait for load", () => MusicController.TrackLoaded);
AddStep("reset timeline size", () => TimelineArea.Timeline.Width = 1); AddStep("reset timeline size", () => TimelineArea.Timeline.Width = 1);
AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange); AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange);

View File

@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Editing
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(30) Colour = OsuColour.Gray(30)
}, },
scrollContainer = new ZoomableScrollContainer scrollContainer = new ZoomableScrollContainer(1, 60, 1)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -80,21 +80,6 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("Inner container width matches scroll container", () => innerBox.DrawWidth == scrollContainer.DrawWidth); AddAssert("Inner container width matches scroll container", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
} }
[Test]
public void TestZoomRangeUpdate()
{
AddStep("set zoom to 2", () => scrollContainer.Zoom = 2);
AddStep("set min zoom to 5", () => scrollContainer.MinZoom = 5);
AddAssert("zoom = 5", () => scrollContainer.Zoom == 5);
AddStep("set max zoom to 10", () => scrollContainer.MaxZoom = 10);
AddAssert("zoom = 5", () => scrollContainer.Zoom == 5);
AddStep("set min zoom to 20", () => scrollContainer.MinZoom = 20);
AddStep("set max zoom to 40", () => scrollContainer.MaxZoom = 40);
AddAssert("zoom = 20", () => scrollContainer.Zoom == 20);
}
[Test] [Test]
public void TestZoom0() public void TestZoom0()
{ {

View File

@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public TestScenePause() public TestScenePause()
{ {
base.Content.Add(content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }); base.Content.Add(content = new GlobalCursorDisplay { RelativeSizeAxes = Axes.Both });
} }
[SetUpSteps] [SetUpSteps]

View File

@ -7,7 +7,6 @@ using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Online; using osu.Game.Online;
using osu.Game.Online.API.Requests.Responses;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions; using osu.Framework.Extensions;
@ -15,7 +14,6 @@ using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
@ -30,9 +28,6 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
private const long online_score_id = 2553163309; private const long online_score_id = 2553163309;
[Resolved]
private RulesetStore rulesets { get; set; }
private TestReplayDownloadButton downloadButton; private TestReplayDownloadButton downloadButton;
[Resolved] [Resolved]
@ -211,21 +206,18 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value); AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
} }
private ScoreInfo getScoreInfo(bool replayAvailable, bool hasOnlineId = true) private ScoreInfo getScoreInfo(bool replayAvailable, bool hasOnlineId = true) => new ScoreInfo
{ {
return new APIScore OnlineID = hasOnlineId ? online_score_id : 0,
Ruleset = new OsuRuleset().RulesetInfo,
BeatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(),
Hash = replayAvailable ? "online" : string.Empty,
User = new APIUser
{ {
OnlineID = hasOnlineId ? online_score_id : 0, Id = 39828,
RulesetID = 0, Username = @"WubWoofWolf",
Beatmap = CreateAPIBeatmapSet(new OsuRuleset().RulesetInfo).Beatmaps.First(), }
HasReplay = replayAvailable, };
User = new APIUser
{
Id = 39828,
Username = @"WubWoofWolf",
}
}.CreateScoreInfo(rulesets, beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First());
}
private class TestReplayDownloadButton : ReplayDownloadButton private class TestReplayDownloadButton : ReplayDownloadButton
{ {

View File

@ -21,12 +21,12 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture] [TestFixture]
public class TestSceneCursors : OsuManualInputManagerTestScene public class TestSceneCursors : OsuManualInputManagerTestScene
{ {
private readonly MenuCursorContainer menuCursorContainer; private readonly GlobalCursorDisplay globalCursorDisplay;
private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6]; private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6];
public TestSceneCursors() public TestSceneCursors()
{ {
Child = menuCursorContainer = new MenuCursorContainer Child = globalCursorDisplay = new GlobalCursorDisplay
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new[] Children = new[]
@ -96,11 +96,11 @@ namespace osu.Game.Tests.Visual.UserInterface
private void testUserCursor() private void testUserCursor()
{ {
AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0])); AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0]));
AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].MenuCursor));
AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor)); AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].MenuCursor));
AddStep("Move out", moveOut); AddStep("Move out", moveOut);
AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].MenuCursor));
AddAssert("Check global cursor visible", () => checkVisible(menuCursorContainer.Cursor)); AddAssert("Check global cursor visible", () => checkVisible(globalCursorDisplay.MenuCursor));
} }
/// <summary> /// <summary>
@ -111,13 +111,13 @@ namespace osu.Game.Tests.Visual.UserInterface
private void testLocalCursor() private void testLocalCursor()
{ {
AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3])); AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3]));
AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].MenuCursor));
AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].MenuCursor));
AddAssert("Check global cursor visible", () => checkVisible(menuCursorContainer.Cursor)); AddAssert("Check global cursor visible", () => checkVisible(globalCursorDisplay.MenuCursor));
AddAssert("Check global cursor at mouse", () => checkAtMouse(menuCursorContainer.Cursor)); AddAssert("Check global cursor at mouse", () => checkAtMouse(globalCursorDisplay.MenuCursor));
AddStep("Move out", moveOut); AddStep("Move out", moveOut);
AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].MenuCursor));
AddAssert("Check global cursor visible", () => checkVisible(menuCursorContainer.Cursor)); AddAssert("Check global cursor visible", () => checkVisible(globalCursorDisplay.MenuCursor));
} }
/// <summary> /// <summary>
@ -128,12 +128,12 @@ namespace osu.Game.Tests.Visual.UserInterface
private void testUserCursorOverride() private void testUserCursorOverride()
{ {
AddStep("Move to blue-green boundary", () => InputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); 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 blue cursor visible", () => checkVisible(cursorBoxes[1].MenuCursor));
AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].MenuCursor));
AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].MenuCursor));
AddStep("Move out", moveOut); AddStep("Move out", moveOut);
AddAssert("Check blue cursor not visible", () => !checkVisible(cursorBoxes[1].Cursor)); AddAssert("Check blue cursor not visible", () => !checkVisible(cursorBoxes[1].MenuCursor));
AddAssert("Check green cursor not visible", () => !checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check green cursor not visible", () => !checkVisible(cursorBoxes[0].MenuCursor));
} }
/// <summary> /// <summary>
@ -143,13 +143,13 @@ namespace osu.Game.Tests.Visual.UserInterface
private void testMultipleLocalCursors() private void testMultipleLocalCursors()
{ {
AddStep("Move to yellow-purple boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); 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 visible", () => checkVisible(cursorBoxes[3].MenuCursor));
AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].MenuCursor));
AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].MenuCursor));
AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].MenuCursor));
AddStep("Move out", moveOut); AddStep("Move out", moveOut);
AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].MenuCursor));
AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].MenuCursor));
} }
/// <summary> /// <summary>
@ -159,13 +159,13 @@ namespace osu.Game.Tests.Visual.UserInterface
private void testUserOverrideWithLocal() private void testUserOverrideWithLocal()
{ {
AddStep("Move to yellow-blue boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); 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 visible", () => checkVisible(cursorBoxes[1].MenuCursor));
AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].MenuCursor));
AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].MenuCursor));
AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].Cursor)); AddAssert("Check yellow cursor at mouse", () => checkAtMouse(cursorBoxes[5].MenuCursor));
AddStep("Move out", moveOut); AddStep("Move out", moveOut);
AddAssert("Check blue cursor invisible", () => !checkVisible(cursorBoxes[1].Cursor)); AddAssert("Check blue cursor invisible", () => !checkVisible(cursorBoxes[1].MenuCursor));
AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].MenuCursor));
} }
/// <summary> /// <summary>
@ -191,7 +191,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
public bool SmoothTransition; public bool SmoothTransition;
public CursorContainer Cursor { get; } public CursorContainer MenuCursor { get; }
public bool ProvidingUserCursor { get; } public bool ProvidingUserCursor { get; }
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || (SmoothTransition && !ProvidingUserCursor); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || (SmoothTransition && !ProvidingUserCursor);
@ -218,7 +218,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Origin = Anchor.Centre, Origin = Anchor.Centre,
Text = providesUserCursor ? "User cursor" : "Local cursor" Text = providesUserCursor ? "User cursor" : "Local cursor"
}, },
Cursor = new TestCursorContainer MenuCursor = new TestCursorContainer
{ {
State = { Value = providesUserCursor ? Visibility.Hidden : Visibility.Visible }, State = { Value = providesUserCursor ? Visibility.Hidden : Visibility.Visible },
} }

View File

@ -6,6 +6,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -27,7 +28,6 @@ namespace osu.Game.Tournament.Tests.NonVisual
{ {
var osu = new TestTournament(runOnLoadComplete: () => var osu = new TestTournament(runOnLoadComplete: () =>
{ {
// ReSharper disable once AccessToDisposedClosure
var storage = host.Storage.GetStorageForDirectory(Path.Combine("tournaments", "default")); var storage = host.Storage.GetStorageForDirectory(Path.Combine("tournaments", "default"));
using (var stream = storage.CreateFileSafely("bracket.json")) using (var stream = storage.CreateFileSafely("bracket.json"))
@ -85,7 +85,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
public new Task BracketLoadTask => base.BracketLoadTask; public new Task BracketLoadTask => base.BracketLoadTask;
public TestTournament(bool resetRuleset = false, Action runOnLoadComplete = null) public TestTournament(bool resetRuleset = false, [InstantHandle] Action runOnLoadComplete = null)
{ {
this.resetRuleset = resetRuleset; this.resetRuleset = resetRuleset;
this.runOnLoadComplete = runOnLoadComplete; this.runOnLoadComplete = runOnLoadComplete;

View File

@ -70,10 +70,10 @@ namespace osu.Game.Tournament
protected override void LoadComplete() protected override void LoadComplete()
{ {
MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display GlobalCursorDisplay.MenuCursor.AlwaysPresent = true; // required for tooltip display
// we don't want to show the menu cursor as it would appear on stream output. // we don't want to show the menu cursor as it would appear on stream output.
MenuCursorContainer.Cursor.Alpha = 0; GlobalCursorDisplay.MenuCursor.Alpha = 0;
base.LoadComplete(); base.LoadComplete();

View File

@ -91,6 +91,7 @@ namespace osu.Game.Configuration
// Input // Input
SetDefault(OsuSetting.MenuCursorSize, 1.0f, 0.5f, 2f, 0.01f); SetDefault(OsuSetting.MenuCursorSize, 1.0f, 0.5f, 2f, 0.01f);
SetDefault(OsuSetting.GameplayCursorSize, 1.0f, 0.1f, 2f, 0.01f); SetDefault(OsuSetting.GameplayCursorSize, 1.0f, 0.1f, 2f, 0.01f);
SetDefault(OsuSetting.GameplayCursorDuringTouch, false);
SetDefault(OsuSetting.AutoCursorSize, false); SetDefault(OsuSetting.AutoCursorSize, false);
SetDefault(OsuSetting.MouseDisableButtons, false); SetDefault(OsuSetting.MouseDisableButtons, false);
@ -292,6 +293,7 @@ namespace osu.Game.Configuration
MenuCursorSize, MenuCursorSize,
GameplayCursorSize, GameplayCursorSize,
AutoCursorSize, AutoCursorSize,
GameplayCursorDuringTouch,
DimLevel, DimLevel,
BlurLevel, BlurLevel,
LightenDuringBreaks, LightenDuringBreaks,

View File

@ -0,0 +1,92 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
using osu.Framework.Input.StateChanges;
using osu.Game.Configuration;
namespace osu.Game.Graphics.Cursor
{
/// <summary>
/// A container which provides the main <see cref="Cursor.MenuCursor"/>.
/// Also handles cases where a more localised cursor is provided by another component (via <see cref="IProvideCursor"/>).
/// </summary>
public class GlobalCursorDisplay : Container, IProvideCursor
{
/// <summary>
/// Control whether any cursor should be displayed.
/// </summary>
internal bool ShowCursor = true;
public CursorContainer MenuCursor { get; }
public bool ProvidingUserCursor => true;
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
private Bindable<bool> showDuringTouch = null!;
private InputManager inputManager = null!;
private IProvideCursor? currentOverrideProvider;
[Resolved]
private OsuConfigManager config { get; set; } = null!;
public GlobalCursorDisplay()
{
AddRangeInternal(new Drawable[]
{
MenuCursor = new MenuCursor { State = { Value = Visibility.Hidden } },
Content = new Container { RelativeSizeAxes = Axes.Both }
});
}
protected override void LoadComplete()
{
base.LoadComplete();
inputManager = GetContainingInputManager();
showDuringTouch = config.GetBindable<bool>(OsuSetting.GameplayCursorDuringTouch);
}
protected override void Update()
{
base.Update();
var lastMouseSource = inputManager.CurrentState.Mouse.LastSource;
bool hasValidInput = lastMouseSource != null && (showDuringTouch.Value || lastMouseSource is not ISourcedFromTouch);
if (!hasValidInput || !ShowCursor)
{
currentOverrideProvider?.MenuCursor?.Hide();
currentOverrideProvider = null;
return;
}
IProvideCursor newOverrideProvider = this;
foreach (var d in inputManager.HoveredDrawables)
{
if (d is IProvideCursor p && p.ProvidingUserCursor)
{
newOverrideProvider = p;
break;
}
}
if (currentOverrideProvider == newOverrideProvider)
return;
currentOverrideProvider?.MenuCursor?.Hide();
newOverrideProvider.MenuCursor?.Show();
currentOverrideProvider = newOverrideProvider;
}
}
}

View File

@ -17,10 +17,10 @@ namespace osu.Game.Graphics.Cursor
/// The cursor provided by this <see cref="IDrawable"/>. /// The cursor provided by this <see cref="IDrawable"/>.
/// May be null if no cursor should be visible. /// May be null if no cursor should be visible.
/// </summary> /// </summary>
CursorContainer Cursor { get; } CursorContainer MenuCursor { get; }
/// <summary> /// <summary>
/// Whether <see cref="Cursor"/> should be displayed as the singular user cursor. This will temporarily hide any other user cursor. /// Whether <see cref="MenuCursor"/> 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). /// This value is checked every frame and may be used to control whether multiple cursors are displayed (e.g. watching replays).
/// </summary> /// </summary>
bool ProvidingUserCursor { get; } bool ProvidingUserCursor { get; }

View File

@ -1,83 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
using osu.Framework.Input.StateChanges;
namespace osu.Game.Graphics.Cursor
{
/// <summary>
/// A container which provides a <see cref="MenuCursor"/> which can be overridden by hovered <see cref="Drawable"/>s.
/// </summary>
public class MenuCursorContainer : Container, IProvideCursor
{
protected override Container<Drawable> Content => content;
private readonly Container content;
/// <summary>
/// Whether any cursors can be displayed.
/// </summary>
internal bool CanShowCursor = true;
public CursorContainer Cursor { get; }
public bool ProvidingUserCursor => true;
public MenuCursorContainer()
{
AddRangeInternal(new Drawable[]
{
Cursor = new MenuCursor { State = { Value = 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();
var lastMouseSource = inputManager.CurrentState.Mouse.LastSource;
bool hasValidInput = lastMouseSource != null && !(lastMouseSource is ISourcedFromTouch);
if (!hasValidInput || !CanShowCursor)
{
currentTarget?.Cursor?.Hide();
currentTarget = null;
return;
}
IProvideCursor newTarget = this;
foreach (var d in inputManager.HoveredDrawables)
{
if (d is IProvideCursor p && p.ProvidingUserCursor)
{
newTarget = p;
break;
}
}
if (currentTarget == newTarget)
return;
currentTarget?.Cursor?.Hide();
newTarget.Cursor?.Show();
currentTarget = newTarget;
}
}
}

View File

@ -34,6 +34,11 @@ namespace osu.Game.Localisation
/// </summary> /// </summary>
public static LocalisableString AutoCursorSize => new TranslatableString(getKey(@"auto_cursor_size"), @"Adjust gameplay cursor size based on current beatmap"); public static LocalisableString AutoCursorSize => new TranslatableString(getKey(@"auto_cursor_size"), @"Adjust gameplay cursor size based on current beatmap");
/// <summary>
/// "Show gameplay cursor during touch input"
/// </summary>
public static LocalisableString GameplayCursorDuringTouch => new TranslatableString(getKey(@"gameplay_cursor_during_touch"), @"Show gameplay cursor during touch input");
/// <summary> /// <summary>
/// "Beatmap skins" /// "Beatmap skins"
/// </summary> /// </summary>

View File

@ -1,162 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Scoring.Legacy;
using osu.Game.Users;
namespace osu.Game.Online.API.Requests.Responses
{
public class APIScore : IScoreInfo
{
[JsonProperty(@"score")]
public long TotalScore { get; set; }
[JsonProperty(@"max_combo")]
public int MaxCombo { get; set; }
[JsonProperty(@"user")]
public APIUser User { get; set; }
[JsonProperty(@"id")]
public long OnlineID { get; set; }
[JsonProperty(@"replay")]
public bool HasReplay { get; set; }
[JsonProperty(@"created_at")]
public DateTimeOffset Date { get; set; }
[JsonProperty(@"beatmap")]
[CanBeNull]
public APIBeatmap Beatmap { get; set; }
[JsonProperty("accuracy")]
public double Accuracy { get; set; }
[JsonProperty(@"pp")]
public double? PP { get; set; }
[JsonProperty(@"beatmapset")]
[CanBeNull]
public APIBeatmapSet BeatmapSet
{
set
{
// in the deserialisation case we need to ferry this data across.
// the order of properties returned by the API guarantees that the beatmap is populated by this point.
if (!(Beatmap is APIBeatmap apiBeatmap))
throw new InvalidOperationException("Beatmap set metadata arrived before beatmap metadata in response");
apiBeatmap.BeatmapSet = value;
}
}
[JsonProperty("statistics")]
public Dictionary<string, int> Statistics { get; set; }
[JsonProperty(@"mode_int")]
public int RulesetID { get; set; }
[JsonProperty(@"mods")]
private string[] mods { set => Mods = value.Select(acronym => new APIMod { Acronym = acronym }); }
[NotNull]
public IEnumerable<APIMod> Mods { get; set; } = Array.Empty<APIMod>();
[JsonProperty("rank")]
[JsonConverter(typeof(StringEnumConverter))]
public ScoreRank Rank { get; set; }
/// <summary>
/// Create a <see cref="ScoreInfo"/> from an API score instance.
/// </summary>
/// <param name="rulesets">A ruleset store, used to populate a ruleset instance in the returned score.</param>
/// <param name="beatmap">An optional beatmap, copied into the returned score (for cases where the API does not populate the beatmap).</param>
/// <returns></returns>
public ScoreInfo CreateScoreInfo(RulesetStore rulesets, BeatmapInfo beatmap = null)
{
var ruleset = rulesets.GetRuleset(RulesetID) ?? throw new InvalidOperationException($"Ruleset with ID of {RulesetID} not found locally");
var rulesetInstance = ruleset.CreateInstance();
var modInstances = Mods.Select(apiMod => rulesetInstance.CreateModFromAcronym(apiMod.Acronym)).Where(m => m != null).ToArray();
// all API scores provided by this class are considered to be legacy.
modInstances = modInstances.Append(rulesetInstance.CreateMod<ModClassic>()).ToArray();
var scoreInfo = new ScoreInfo
{
TotalScore = TotalScore,
MaxCombo = MaxCombo,
BeatmapInfo = beatmap ?? new BeatmapInfo(),
User = User,
Accuracy = Accuracy,
OnlineID = OnlineID,
Date = Date,
PP = PP,
Hash = HasReplay ? "online" : string.Empty, // todo: temporary?
Rank = Rank,
Ruleset = ruleset,
Mods = modInstances,
};
if (Statistics != null)
{
foreach (var kvp in Statistics)
{
switch (kvp.Key)
{
case @"count_geki":
scoreInfo.SetCountGeki(kvp.Value);
break;
case @"count_300":
scoreInfo.SetCount300(kvp.Value);
break;
case @"count_katu":
scoreInfo.SetCountKatu(kvp.Value);
break;
case @"count_100":
scoreInfo.SetCount100(kvp.Value);
break;
case @"count_50":
scoreInfo.SetCount50(kvp.Value);
break;
case @"count_miss":
scoreInfo.SetCountMiss(kvp.Value);
break;
}
}
}
return scoreInfo;
}
public IRulesetInfo Ruleset => new RulesetInfo { OnlineID = RulesetID };
IEnumerable<INamedFileUsage> IHasNamedFiles.Files => throw new NotImplementedException();
#region Implementation of IScoreInfo
IBeatmapInfo IScoreInfo.Beatmap => Beatmap;
IUser IScoreInfo.User => User;
#endregion
}
}

View File

@ -151,6 +151,23 @@ namespace osu.Game.Online.API.Requests.Responses
PP = PP, PP = PP,
}; };
/// <summary>
/// Creates a <see cref="SoloScoreInfo"/> from a local score for score submission.
/// </summary>
/// <param name="score">The local score.</param>
public static SoloScoreInfo ForSubmission(ScoreInfo score) => new SoloScoreInfo
{
Rank = score.Rank,
TotalScore = (int)score.TotalScore,
Accuracy = score.Accuracy,
PP = score.PP,
MaxCombo = score.MaxCombo,
RulesetID = score.RulesetID,
Passed = score.Passed,
Mods = score.APIMods,
Statistics = score.Statistics,
};
public long OnlineID => ID ?? -1; public long OnlineID => ID ?? -1;
} }
} }

View File

@ -7,20 +7,20 @@ using System.Net.Http;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.Solo; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Scoring; using osu.Game.Scoring;
namespace osu.Game.Online.Rooms namespace osu.Game.Online.Rooms
{ {
public abstract class SubmitScoreRequest : APIRequest<MultiplayerScore> public abstract class SubmitScoreRequest : APIRequest<MultiplayerScore>
{ {
public readonly SubmittableScore Score; public readonly SoloScoreInfo Score;
protected readonly long ScoreId; protected readonly long ScoreId;
protected SubmitScoreRequest(ScoreInfo scoreInfo, long scoreId) protected SubmitScoreRequest(ScoreInfo scoreInfo, long scoreId)
{ {
Score = new SubmittableScore(scoreInfo); Score = SoloScoreInfo.ForSubmission(scoreInfo);
ScoreId = scoreId; ScoreId = scoreId;
} }

View File

@ -1,72 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
namespace osu.Game.Online.Solo
{
/// <summary>
/// A class specifically for sending scores to the API during score submission.
/// This is used instead of <see cref="APIScore"/> due to marginally different serialisation naming requirements.
/// </summary>
[Serializable]
public class SubmittableScore
{
[JsonProperty("rank")]
[JsonConverter(typeof(StringEnumConverter))]
public ScoreRank Rank { get; set; }
[JsonProperty("total_score")]
public long TotalScore { get; set; }
[JsonProperty("accuracy")]
public double Accuracy { get; set; }
[JsonProperty(@"pp")]
public double? PP { get; set; }
[JsonProperty("max_combo")]
public int MaxCombo { get; set; }
[JsonProperty("ruleset_id")]
public int RulesetID { get; set; }
[JsonProperty("passed")]
public bool Passed { get; set; }
// Used for API serialisation/deserialisation.
[JsonProperty("mods")]
public APIMod[] Mods { get; set; }
[JsonProperty("statistics")]
public Dictionary<HitResult, int> Statistics { get; set; }
[UsedImplicitly]
public SubmittableScore()
{
}
public SubmittableScore(ScoreInfo score)
{
Rank = score.Rank;
TotalScore = score.TotalScore;
Accuracy = score.Accuracy;
PP = score.PP;
MaxCombo = score.MaxCombo;
RulesetID = score.RulesetID;
Passed = score.Passed;
Mods = score.APIMods;
Statistics = score.Statistics;
}
}
}

View File

@ -716,7 +716,7 @@ namespace osu.Game
// The next time this is updated is in UpdateAfterChildren, which occurs too late and results // 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. // 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 // This prevents the cursor from showing until we have a screen with CursorVisible = true
MenuCursorContainer.CanShowCursor = menuScreen?.CursorVisible ?? false; GlobalCursorDisplay.ShowCursor = menuScreen?.CursorVisible ?? false;
// todo: all archive managers should be able to be looped here. // todo: all archive managers should be able to be looped here.
SkinManager.PostNotification = n => Notifications.Post(n); SkinManager.PostNotification = n => Notifications.Post(n);
@ -1231,7 +1231,7 @@ namespace osu.Game
ScreenOffsetContainer.X = horizontalOffset; ScreenOffsetContainer.X = horizontalOffset;
overlayContent.X = horizontalOffset * 1.2f; overlayContent.X = horizontalOffset * 1.2f;
MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false; GlobalCursorDisplay.ShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
} }
private void screenChanged(IScreen current, IScreen newScreen) private void screenChanged(IScreen current, IScreen newScreen)

View File

@ -138,7 +138,7 @@ namespace osu.Game
protected RealmKeyBindingStore KeyBindingStore { get; private set; } protected RealmKeyBindingStore KeyBindingStore { get; private set; }
protected MenuCursorContainer MenuCursorContainer { get; private set; } protected GlobalCursorDisplay GlobalCursorDisplay { get; private set; }
protected MusicController MusicController { get; private set; } protected MusicController MusicController { get; private set; }
@ -340,10 +340,10 @@ namespace osu.Game
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = CreateScalingContainer().WithChildren(new Drawable[] Child = CreateScalingContainer().WithChildren(new Drawable[]
{ {
(MenuCursorContainer = new MenuCursorContainer (GlobalCursorDisplay = new GlobalCursorDisplay
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}).WithChild(content = new OsuTooltipContainer(MenuCursorContainer.Cursor) }).WithChild(content = new OsuTooltipContainer(GlobalCursorDisplay.MenuCursor)
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}), }),

View File

@ -32,6 +32,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
LabelText = SkinSettingsStrings.AutoCursorSize, LabelText = SkinSettingsStrings.AutoCursorSize,
Current = config.GetBindable<bool>(OsuSetting.AutoCursorSize) Current = config.GetBindable<bool>(OsuSetting.AutoCursorSize)
}, },
new SettingsCheckbox
{
LabelText = SkinSettingsStrings.GameplayCursorDuringTouch,
Current = config.GetBindable<bool>(OsuSetting.GameplayCursorDuringTouch)
},
}; };
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows) if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)

View File

@ -380,7 +380,7 @@ namespace osu.Game.Rulesets.UI
// only show the cursor when within the playfield, by default. // only show the cursor when within the playfield, by default.
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Playfield.ReceivePositionalInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Playfield.ReceivePositionalInputAt(screenSpacePos);
CursorContainer IProvideCursor.Cursor => Playfield.Cursor; CursorContainer IProvideCursor.MenuCursor => Playfield.Cursor;
public override GameplayCursorContainer Cursor => Playfield.Cursor; public override GameplayCursorContainer Cursor => Playfield.Cursor;

View File

@ -146,13 +146,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
waveform.Waveform = b.NewValue.Waveform; waveform.Waveform = b.NewValue.Waveform;
track = b.NewValue.Track; track = b.NewValue.Track;
// todo: i don't think this is safe, the track may not be loaded yet. setupTimelineZoom();
if (track.Length > 0)
{
MaxZoom = getZoomLevelForVisibleMilliseconds(500);
MinZoom = getZoomLevelForVisibleMilliseconds(10000);
defaultTimelineZoom = getZoomLevelForVisibleMilliseconds(6000);
}
}, true); }, true);
Zoom = (float)(defaultTimelineZoom * editorBeatmap.BeatmapInfo.TimelineZoom); Zoom = (float)(defaultTimelineZoom * editorBeatmap.BeatmapInfo.TimelineZoom);
@ -205,6 +199,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
scrollToTrackTime(); scrollToTrackTime();
} }
private void setupTimelineZoom()
{
if (!track.IsLoaded)
{
Scheduler.AddOnce(setupTimelineZoom);
return;
}
defaultTimelineZoom = getZoomLevelForVisibleMilliseconds(6000);
float initialZoom = (float)(defaultTimelineZoom * editorBeatmap.BeatmapInfo.TimelineZoom);
SetupZoom(initialZoom, getZoomLevelForVisibleMilliseconds(10000), getZoomLevelForVisibleMilliseconds(500));
}
protected override bool OnScroll(ScrollEvent e) protected override bool OnScroll(ScrollEvent e)
{ {
// if this is not a precision scroll event, let the editor handle the seek itself (for snapping support) // if this is not a precision scroll event, let the editor handle the seek itself (for snapping support)

View File

@ -32,20 +32,28 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private readonly Container zoomedContent; private readonly Container zoomedContent;
protected override Container<Drawable> Content => zoomedContent; protected override Container<Drawable> Content => zoomedContent;
private float currentZoom = 1;
/// <summary> /// <summary>
/// The current zoom level of <see cref="ZoomableScrollContainer" />. /// The current zoom level of <see cref="ZoomableScrollContainer"/>.
/// It may differ from <see cref="Zoom" /> during transitions. /// It may differ from <see cref="Zoom"/> during transitions.
/// </summary> /// </summary>
public float CurrentZoom => currentZoom; public float CurrentZoom { get; private set; } = 1;
private bool isZoomSetUp;
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private IFrameBasedClock editorClock { get; set; } private IFrameBasedClock editorClock { get; set; }
private readonly LayoutValue zoomedContentWidthCache = new LayoutValue(Invalidation.DrawSize); private readonly LayoutValue zoomedContentWidthCache = new LayoutValue(Invalidation.DrawSize);
public ZoomableScrollContainer() private float minZoom;
private float maxZoom;
/// <summary>
/// Creates a <see cref="ZoomableScrollContainer"/> with no zoom range.
/// Functionality will be disabled until zoom is set up via <see cref="SetupZoom"/>.
/// </summary>
protected ZoomableScrollContainer()
: base(Direction.Horizontal) : base(Direction.Horizontal)
{ {
base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y }); base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y });
@ -53,46 +61,36 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
AddLayout(zoomedContentWidthCache); AddLayout(zoomedContentWidthCache);
} }
private float minZoom = 1;
/// <summary> /// <summary>
/// The minimum zoom level allowed. /// Creates a <see cref="ZoomableScrollContainer"/> with a defined zoom range.
/// </summary> /// </summary>
public float MinZoom public ZoomableScrollContainer(float minimum, float maximum, float initial)
: this()
{ {
get => minZoom; SetupZoom(initial, minimum, maximum);
set
{
if (value < 1)
throw new ArgumentException($"{nameof(MinZoom)} must be >= 1.", nameof(value));
minZoom = value;
// ensure zoom range is in valid state before updating zoom.
if (MinZoom < MaxZoom)
updateZoom();
}
} }
private float maxZoom = 60;
/// <summary> /// <summary>
/// The maximum zoom level allowed. /// Sets up the minimum and maximum range of this zoomable scroll container, along with the initial zoom value.
/// </summary> /// </summary>
public float MaxZoom /// <param name="initial">The initial zoom value, applied immediately.</param>
/// <param name="minimum">The minimum zoom value.</param>
/// <param name="maximum">The maximum zoom value.</param>
protected void SetupZoom(float initial, float minimum, float maximum)
{ {
get => maxZoom; if (minimum < 1)
set throw new ArgumentException($"{nameof(minimum)} ({minimum}) must be >= 1.", nameof(maximum));
{
if (value < 1)
throw new ArgumentException($"{nameof(MaxZoom)} must be >= 1.", nameof(value));
maxZoom = value; if (maximum < 1)
throw new ArgumentException($"{nameof(maximum)} ({maximum}) must be >= 1.", nameof(maximum));
// ensure zoom range is in valid state before updating zoom. if (minimum > maximum)
if (MaxZoom > MinZoom) throw new ArgumentException($"{nameof(minimum)} ({minimum}) must be less than {nameof(maximum)} ({maximum})");
updateZoom();
} minZoom = minimum;
maxZoom = maximum;
CurrentZoom = zoomTarget = initial;
isZoomSetUp = true;
} }
/// <summary> /// <summary>
@ -104,14 +102,17 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
set => updateZoom(value); set => updateZoom(value);
} }
private void updateZoom(float? value = null) private void updateZoom(float value)
{ {
float newZoom = Math.Clamp(value ?? Zoom, MinZoom, MaxZoom); if (!isZoomSetUp)
return;
float newZoom = Math.Clamp(value, minZoom, maxZoom);
if (IsLoaded) if (IsLoaded)
setZoomTarget(newZoom, ToSpaceOfOtherDrawable(new Vector2(DrawWidth / 2, 0), zoomedContent).X); setZoomTarget(newZoom, ToSpaceOfOtherDrawable(new Vector2(DrawWidth / 2, 0), zoomedContent).X);
else else
currentZoom = zoomTarget = newZoom; CurrentZoom = zoomTarget = newZoom;
} }
protected override void Update() protected override void Update()
@ -141,22 +142,25 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private void updateZoomedContentWidth() private void updateZoomedContentWidth()
{ {
zoomedContent.Width = DrawWidth * currentZoom; zoomedContent.Width = DrawWidth * CurrentZoom;
zoomedContentWidthCache.Validate(); zoomedContentWidthCache.Validate();
} }
public void AdjustZoomRelatively(float change, float? focusPoint = null) public void AdjustZoomRelatively(float change, float? focusPoint = null)
{ {
if (!isZoomSetUp)
return;
const float zoom_change_sensitivity = 0.02f; const float zoom_change_sensitivity = 0.02f;
setZoomTarget(zoomTarget + change * (MaxZoom - minZoom) * zoom_change_sensitivity, focusPoint); setZoomTarget(zoomTarget + change * (maxZoom - minZoom) * zoom_change_sensitivity, focusPoint);
} }
private float zoomTarget = 1; private float zoomTarget = 1;
private void setZoomTarget(float newZoom, float? focusPoint = null) private void setZoomTarget(float newZoom, float? focusPoint = null)
{ {
zoomTarget = Math.Clamp(newZoom, MinZoom, MaxZoom); zoomTarget = Math.Clamp(newZoom, minZoom, maxZoom);
focusPoint ??= zoomedContent.ToLocalSpace(ToScreenSpace(new Vector2(DrawWidth / 2, 0))).X; focusPoint ??= zoomedContent.ToLocalSpace(ToScreenSpace(new Vector2(DrawWidth / 2, 0))).X;
transformZoomTo(zoomTarget, focusPoint.Value, ZoomDuration, ZoomEasing); transformZoomTo(zoomTarget, focusPoint.Value, ZoomDuration, ZoomEasing);
@ -192,7 +196,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private readonly float scrollOffset; private readonly float scrollOffset;
/// <summary> /// <summary>
/// Transforms <see cref="ZoomableScrollContainer.currentZoom"/> to a new value. /// Transforms <see cref="ZoomableScrollContainer.CurrentZoom"/> to a new value.
/// </summary> /// </summary>
/// <param name="focusPoint">The focus point in absolute coordinates local to the content.</param> /// <param name="focusPoint">The focus point in absolute coordinates local to the content.</param>
/// <param name="contentSize">The size of the content.</param> /// <param name="contentSize">The size of the content.</param>
@ -204,7 +208,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
this.scrollOffset = scrollOffset; this.scrollOffset = scrollOffset;
} }
public override string TargetMember => nameof(currentZoom); public override string TargetMember => nameof(CurrentZoom);
private float valueAt(double time) private float valueAt(double time)
{ {
@ -222,7 +226,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
float expectedWidth = d.DrawWidth * newZoom; float expectedWidth = d.DrawWidth * newZoom;
float targetOffset = expectedWidth * (focusPoint / contentSize) - focusOffset; float targetOffset = expectedWidth * (focusPoint / contentSize) - focusOffset;
d.currentZoom = newZoom; d.CurrentZoom = newZoom;
d.updateZoomedContentWidth(); d.updateZoomedContentWidth();
// Temporarily here to make sure ScrollTo gets the correct DrawSize for scrollable area. // Temporarily here to make sure ScrollTo gets the correct DrawSize for scrollable area.
@ -231,7 +235,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
d.ScrollTo(targetOffset, false); d.ScrollTo(targetOffset, false);
} }
protected override void ReadIntoStartValue(ZoomableScrollContainer d) => StartValue = d.currentZoom; protected override void ReadIntoStartValue(ZoomableScrollContainer d) => StartValue = d.CurrentZoom;
} }
} }
} }

View File

@ -11,6 +11,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
@ -90,18 +91,16 @@ namespace osu.Game.Screens.Ranking.Statistics
spinner.Show(); spinner.Show();
var localCancellationSource = loadCancellation = new CancellationTokenSource(); var localCancellationSource = loadCancellation = new CancellationTokenSource();
IBeatmap playableBeatmap = null;
var workingBeatmap = beatmapManager.GetWorkingBeatmap(newScore.BeatmapInfo);
// Todo: The placement of this is temporary. Eventually we'll both generate the playable beatmap _and_ run through it in a background task to generate the hit events. // Todo: The placement of this is temporary. Eventually we'll both generate the playable beatmap _and_ run through it in a background task to generate the hit events.
Task.Run(() => Task.Run(() => workingBeatmap.GetPlayableBeatmap(newScore.Ruleset, newScore.Mods), loadCancellation.Token).ContinueWith(task => Schedule(() =>
{
playableBeatmap = beatmapManager.GetWorkingBeatmap(newScore.BeatmapInfo).GetPlayableBeatmap(newScore.Ruleset, newScore.Mods);
}, loadCancellation.Token).ContinueWith(_ => Schedule(() =>
{ {
bool hitEventsAvailable = newScore.HitEvents.Count != 0; bool hitEventsAvailable = newScore.HitEvents.Count != 0;
Container<Drawable> container; Container<Drawable> container;
var statisticRows = newScore.Ruleset.CreateInstance().CreateStatisticsForScore(newScore, playableBeatmap); var statisticRows = newScore.Ruleset.CreateInstance().CreateStatisticsForScore(newScore, task.GetResultSafely());
if (!hitEventsAvailable && statisticRows.SelectMany(r => r.Columns).All(c => c.RequiresHitEvents)) if (!hitEventsAvailable && statisticRows.SelectMany(r => r.Columns).All(c => c.RequiresHitEvents))
{ {

View File

@ -36,7 +36,7 @@ namespace osu.Game.Screens.Utility
public readonly Bindable<LatencyVisualMode> VisualMode = new Bindable<LatencyVisualMode>(); public readonly Bindable<LatencyVisualMode> VisualMode = new Bindable<LatencyVisualMode>();
public CursorContainer? Cursor { get; private set; } public CursorContainer? MenuCursor { get; private set; }
public bool ProvidingUserCursor => IsActiveArea.Value; public bool ProvidingUserCursor => IsActiveArea.Value;
@ -91,7 +91,7 @@ namespace osu.Game.Screens.Utility
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
Cursor = new LatencyCursorContainer MenuCursor = new LatencyCursorContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
@ -105,7 +105,7 @@ namespace osu.Game.Screens.Utility
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
Cursor = new LatencyCursorContainer MenuCursor = new LatencyCursorContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
@ -119,7 +119,7 @@ namespace osu.Game.Screens.Utility
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
Cursor = new LatencyCursorContainer MenuCursor = new LatencyCursorContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },

View File

@ -38,11 +38,11 @@ namespace osu.Game.Tests.Visual
protected OsuManualInputManagerTestScene() protected OsuManualInputManagerTestScene()
{ {
MenuCursorContainer cursorContainer; GlobalCursorDisplay cursorDisplay;
CompositeDrawable mainContent = cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }; CompositeDrawable mainContent = cursorDisplay = new GlobalCursorDisplay { RelativeSizeAxes = Axes.Both };
cursorContainer.Child = content = new OsuTooltipContainer(cursorContainer.Cursor) cursorDisplay.Child = content = new OsuTooltipContainer(cursorDisplay.MenuCursor)
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}; };