mirror of
https://github.com/ppy/osu.git
synced 2025-02-13 10:33:07 +08:00
Merge branch 'master' into multi-queueing-modes
This commit is contained in:
commit
cb2547a6be
@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1112.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1108.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1118.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
114
osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs
Normal file
114
osu.Game.Tests/Beatmaps/WorkingBeatmapTest.cs
Normal file
@ -0,0 +1,114 @@
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
[TestFixture]
|
||||
public class WorkingBeatmapTest
|
||||
{
|
||||
[Test]
|
||||
public void TestGetPlayableSuccess()
|
||||
{
|
||||
var working = new TestNeverLoadsWorkingBeatmap();
|
||||
|
||||
working.ResetEvent.Set();
|
||||
|
||||
Assert.NotNull(working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGetPlayableCancellationToken()
|
||||
{
|
||||
var working = new TestNeverLoadsWorkingBeatmap();
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
var loadStarted = new ManualResetEventSlim();
|
||||
var loadCompleted = new ManualResetEventSlim();
|
||||
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
loadStarted.Set();
|
||||
Assert.Throws<OperationCanceledException>(() => working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo, cancellationToken: cts.Token));
|
||||
loadCompleted.Set();
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
|
||||
Assert.IsTrue(loadStarted.Wait(10000));
|
||||
|
||||
cts.Cancel();
|
||||
|
||||
Assert.IsTrue(loadCompleted.Wait(10000));
|
||||
|
||||
working.ResetEvent.Set();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGetPlayableDefaultTimeout()
|
||||
{
|
||||
var working = new TestNeverLoadsWorkingBeatmap();
|
||||
|
||||
Assert.Throws<OperationCanceledException>(() => working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo));
|
||||
|
||||
working.ResetEvent.Set();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGetPlayableRulesetLoadFailure()
|
||||
{
|
||||
var working = new TestWorkingBeatmap(new Beatmap());
|
||||
|
||||
// by default mocks return nulls if not set up, which is actually desired here to simulate a ruleset load failure scenario.
|
||||
var ruleset = new Mock<IRulesetInfo>();
|
||||
|
||||
Assert.Throws<RulesetLoadException>(() => working.GetPlayableBeatmap(ruleset.Object));
|
||||
}
|
||||
|
||||
public class TestNeverLoadsWorkingBeatmap : TestWorkingBeatmap
|
||||
{
|
||||
public ManualResetEventSlim ResetEvent = new ManualResetEventSlim();
|
||||
|
||||
public TestNeverLoadsWorkingBeatmap()
|
||||
: base(new Beatmap())
|
||||
{
|
||||
}
|
||||
|
||||
protected override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => new TestConverter(beatmap, ResetEvent);
|
||||
|
||||
public class TestConverter : IBeatmapConverter
|
||||
{
|
||||
private readonly ManualResetEventSlim resetEvent;
|
||||
|
||||
public TestConverter(IBeatmap beatmap, ManualResetEventSlim resetEvent)
|
||||
{
|
||||
this.resetEvent = resetEvent;
|
||||
Beatmap = beatmap;
|
||||
}
|
||||
|
||||
public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
|
||||
|
||||
protected virtual void OnObjectConverted(HitObject arg1, IEnumerable<HitObject> arg2) => ObjectConverted?.Invoke(arg1, arg2);
|
||||
|
||||
public IBeatmap Beatmap { get; }
|
||||
|
||||
public bool CanConvert() => true;
|
||||
|
||||
public IBeatmap Convert(CancellationToken cancellationToken = default)
|
||||
{
|
||||
resetEvent.Wait(cancellationToken);
|
||||
return new OsuBeatmap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -83,6 +83,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<TestAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
ReceivedAction = e.Action == TestAction.Down;
|
||||
return true;
|
||||
}
|
||||
|
@ -231,6 +231,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<TestAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
box.Colour = Color4.White;
|
||||
return true;
|
||||
}
|
||||
|
@ -164,6 +164,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<TestAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
box.Colour = Color4.White;
|
||||
return true;
|
||||
}
|
||||
|
@ -283,6 +283,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<TestAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
box.Colour = Color4.White;
|
||||
return true;
|
||||
}
|
||||
|
@ -140,21 +140,21 @@ namespace osu.Game.Beatmaps
|
||||
return GetAsync(new DifficultyCacheLookup(localBeatmapInfo, localRulesetInfo, mods), cancellationToken);
|
||||
}
|
||||
|
||||
protected override Task<StarDifficulty> ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default)
|
||||
protected override Task<StarDifficulty> ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.Factory.StartNew(() =>
|
||||
{
|
||||
if (CheckExists(lookup, out var existing))
|
||||
return existing;
|
||||
|
||||
return computeDifficulty(lookup);
|
||||
}, token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
|
||||
return computeDifficulty(lookup, cancellationToken);
|
||||
}, cancellationToken, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
|
||||
}
|
||||
|
||||
public Task<List<TimedDifficultyAttributes>> GetTimedDifficultyAttributesAsync(IWorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken token = default)
|
||||
public Task<List<TimedDifficultyAttributes>> GetTimedDifficultyAttributesAsync(IWorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods),
|
||||
token,
|
||||
return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods, cancellationToken),
|
||||
cancellationToken,
|
||||
TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously,
|
||||
updateScheduler);
|
||||
}
|
||||
@ -270,8 +270,9 @@ namespace osu.Game.Beatmaps
|
||||
/// Computes the difficulty defined by a <see cref="DifficultyCacheLookup"/> key, and stores it to the timed cache.
|
||||
/// </summary>
|
||||
/// <param name="key">The <see cref="DifficultyCacheLookup"/> that defines the computation parameters.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The <see cref="StarDifficulty"/>.</returns>
|
||||
private StarDifficulty computeDifficulty(in DifficultyCacheLookup key)
|
||||
private StarDifficulty computeDifficulty(in DifficultyCacheLookup key, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset.
|
||||
var beatmapInfo = key.BeatmapInfo;
|
||||
@ -283,7 +284,7 @@ namespace osu.Game.Beatmaps
|
||||
Debug.Assert(ruleset != null);
|
||||
|
||||
var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(key.BeatmapInfo));
|
||||
var attributes = calculator.Calculate(key.OrderedMods);
|
||||
var attributes = calculator.Calculate(key.OrderedMods, cancellationToken);
|
||||
|
||||
return new StarDifficulty(attributes);
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Rulesets;
|
||||
@ -92,10 +92,10 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
/// <param name="ruleset">The <see cref="RulesetInfo"/> to create a playable <see cref="IBeatmap"/> for.</param>
|
||||
/// <param name="mods">The <see cref="Mod"/>s to apply to the <see cref="IBeatmap"/>.</param>
|
||||
/// <param name="timeout">The maximum length in milliseconds to wait for load to complete. Defaults to 10,000ms.</param>
|
||||
/// <param name="cancellationToken">Cancellation token that cancels the beatmap loading process. If not provided, a default timeout of 10,000ms will be applied to the load process.</param>
|
||||
/// <returns>The converted <see cref="IBeatmap"/>.</returns>
|
||||
/// <exception cref="BeatmapInvalidForRulesetException">If <see cref="Beatmap"/> could not be converted to <paramref name="ruleset"/>.</exception>
|
||||
IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null);
|
||||
IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList<Mod> mods = null, CancellationToken? cancellationToken = null);
|
||||
|
||||
/// <summary>
|
||||
/// Load a new audio track instance for this beatmap. This should be called once before accessing <see cref="Track"/>.
|
||||
|
@ -79,100 +79,101 @@ namespace osu.Game.Beatmaps
|
||||
/// <returns>The applicable <see cref="IBeatmapConverter"/>.</returns>
|
||||
protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap);
|
||||
|
||||
public virtual IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
|
||||
public virtual IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList<Mod> mods = null, CancellationToken? cancellationToken = null)
|
||||
{
|
||||
using (var cancellationSource = createCancellationTokenSource(timeout))
|
||||
var token = cancellationToken ??
|
||||
// don't apply the default timeout when debugger is attached (may be breakpointing / debugging).
|
||||
(Debugger.IsAttached ? new CancellationToken() : new CancellationTokenSource(10000).Token);
|
||||
|
||||
mods ??= Array.Empty<Mod>();
|
||||
|
||||
var rulesetInstance = ruleset.CreateInstance();
|
||||
|
||||
if (rulesetInstance == null)
|
||||
throw new RulesetLoadException("Creating ruleset instance failed when attempting to create playable beatmap.");
|
||||
|
||||
IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance);
|
||||
|
||||
// Check if the beatmap can be converted
|
||||
if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert())
|
||||
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
|
||||
|
||||
// Apply conversion mods
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmapConverter>())
|
||||
{
|
||||
mods ??= Array.Empty<Mod>();
|
||||
|
||||
var rulesetInstance = ruleset.CreateInstance();
|
||||
|
||||
if (rulesetInstance == null)
|
||||
throw new RulesetLoadException("Creating ruleset instance failed when attempting to create playable beatmap.");
|
||||
|
||||
IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance);
|
||||
|
||||
// Check if the beatmap can be converted
|
||||
if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert())
|
||||
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
|
||||
|
||||
// Apply conversion mods
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmapConverter>())
|
||||
{
|
||||
if (cancellationSource.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
mod.ApplyToBeatmapConverter(converter);
|
||||
}
|
||||
|
||||
// Convert
|
||||
IBeatmap converted = converter.Convert(cancellationSource.Token);
|
||||
|
||||
// Apply conversion mods to the result
|
||||
foreach (var mod in mods.OfType<IApplicableAfterBeatmapConversion>())
|
||||
{
|
||||
if (cancellationSource.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
mod.ApplyToBeatmap(converted);
|
||||
}
|
||||
|
||||
// Apply difficulty mods
|
||||
if (mods.Any(m => m is IApplicableToDifficulty))
|
||||
{
|
||||
foreach (var mod in mods.OfType<IApplicableToDifficulty>())
|
||||
{
|
||||
if (cancellationSource.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
mod.ApplyToDifficulty(converted.Difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted);
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmapProcessor>())
|
||||
mod.ApplyToBeatmapProcessor(processor);
|
||||
|
||||
processor?.PreProcess();
|
||||
|
||||
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
|
||||
try
|
||||
{
|
||||
foreach (var obj in converted.HitObjects)
|
||||
{
|
||||
if (cancellationSource.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, cancellationSource.Token);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
}
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToHitObject>())
|
||||
{
|
||||
foreach (var obj in converted.HitObjects)
|
||||
{
|
||||
if (cancellationSource.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
mod.ApplyToHitObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
processor?.PostProcess();
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmap>())
|
||||
{
|
||||
cancellationSource.Token.ThrowIfCancellationRequested();
|
||||
mod.ApplyToBeatmap(converted);
|
||||
}
|
||||
|
||||
return converted;
|
||||
mod.ApplyToBeatmapConverter(converter);
|
||||
}
|
||||
|
||||
// Convert
|
||||
IBeatmap converted = converter.Convert(token);
|
||||
|
||||
// Apply conversion mods to the result
|
||||
foreach (var mod in mods.OfType<IApplicableAfterBeatmapConversion>())
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
mod.ApplyToBeatmap(converted);
|
||||
}
|
||||
|
||||
// Apply difficulty mods
|
||||
if (mods.Any(m => m is IApplicableToDifficulty))
|
||||
{
|
||||
foreach (var mod in mods.OfType<IApplicableToDifficulty>())
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
mod.ApplyToDifficulty(converted.Difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted);
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmapProcessor>())
|
||||
mod.ApplyToBeatmapProcessor(processor);
|
||||
|
||||
processor?.PreProcess();
|
||||
|
||||
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
|
||||
try
|
||||
{
|
||||
foreach (var obj in converted.HitObjects)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
obj.ApplyDefaults(converted.ControlPointInfo, converted.Difficulty, token);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
}
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToHitObject>())
|
||||
{
|
||||
foreach (var obj in converted.HitObjects)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
mod.ApplyToHitObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
processor?.PostProcess();
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmap>())
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
mod.ApplyToBeatmap(converted);
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
private CancellationTokenSource loadCancellation = new CancellationTokenSource();
|
||||
@ -191,15 +192,6 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
private CancellationTokenSource createCancellationTokenSource(TimeSpan? timeout)
|
||||
{
|
||||
if (Debugger.IsAttached)
|
||||
// ignore timeout when debugger is attached (may be breakpointing / debugging).
|
||||
return new CancellationTokenSource();
|
||||
|
||||
return new CancellationTokenSource(timeout ?? TimeSpan.FromSeconds(10));
|
||||
}
|
||||
|
||||
private readonly object beatmapFetchLock = new object();
|
||||
|
||||
private Task<IBeatmap> loadBeatmapAsync()
|
||||
|
@ -1,11 +1,8 @@
|
||||
// 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 System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
@ -14,30 +11,6 @@ namespace osu.Game.Extensions
|
||||
{
|
||||
public static class DrawableExtensions
|
||||
{
|
||||
public const double REPEAT_INTERVAL = 70;
|
||||
public const double INITIAL_DELAY = 250;
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that is used while <see cref="IKeyBindingHandler"/> doesn't support repetitions of <see cref="IKeyBindingHandler{T}.OnPressed"/>.
|
||||
/// Simulates repetitions by continually invoking a delegate according to the default key repeat rate.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The returned delegate can be cancelled to stop repeat events from firing (usually in <see cref="IKeyBindingHandler{T}.OnReleased"/>).
|
||||
/// </remarks>
|
||||
/// <param name="handler">The <see cref="IKeyBindingHandler{T}"/> which is handling the repeat.</param>
|
||||
/// <param name="scheduler">The <see cref="Scheduler"/> to schedule repetitions on.</param>
|
||||
/// <param name="action">The <see cref="Action"/> to be invoked once immediately and with every repetition.</param>
|
||||
/// <param name="initialRepeatDelay">The delay imposed on the first repeat. Defaults to <see cref="INITIAL_DELAY"/>.</param>
|
||||
/// <returns>A <see cref="ScheduledDelegate"/> which can be cancelled to stop the repeat events from firing.</returns>
|
||||
public static ScheduledDelegate BeginKeyRepeat(this IKeyBindingHandler handler, Scheduler scheduler, Action action, double initialRepeatDelay = INITIAL_DELAY)
|
||||
{
|
||||
action();
|
||||
|
||||
ScheduledDelegate repeatDelegate = new ScheduledDelegate(action, handler.Time.Current + initialRepeatDelay, REPEAT_INTERVAL);
|
||||
scheduler.Add(repeatDelegate);
|
||||
return repeatDelegate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shakes this drawable.
|
||||
/// </summary>
|
||||
|
@ -90,6 +90,9 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Back:
|
||||
|
@ -60,6 +60,9 @@ namespace osu.Game.Graphics
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.TakeScreenshot:
|
||||
|
@ -64,6 +64,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Back:
|
||||
|
@ -75,6 +75,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
if (!HasFocus) return false;
|
||||
|
||||
if (e.Action == GlobalAction.Back)
|
||||
|
@ -58,6 +58,9 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
if (State.Value == Visibility.Hidden)
|
||||
return false;
|
||||
|
||||
|
@ -1007,6 +1007,9 @@ namespace osu.Game
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
if (introScreen == null) return false;
|
||||
|
||||
switch (e.Action)
|
||||
|
@ -94,6 +94,9 @@ namespace osu.Game.Overlays
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Back:
|
||||
|
@ -102,6 +102,9 @@ namespace osu.Game.Overlays
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Select:
|
||||
|
@ -32,6 +32,9 @@ namespace osu.Game.Overlays.Music
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
if (beatmap.Disabled)
|
||||
return false;
|
||||
|
||||
|
@ -6,8 +6,6 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Input.Bindings;
|
||||
|
||||
namespace osu.Game.Overlays.Volume
|
||||
@ -17,18 +15,12 @@ namespace osu.Game.Overlays.Volume
|
||||
public Func<GlobalAction, bool> ActionRequested;
|
||||
public Func<GlobalAction, float, bool, bool> ScrollActionRequested;
|
||||
|
||||
private ScheduledDelegate keyRepeat;
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.DecreaseVolume:
|
||||
case GlobalAction.IncreaseVolume:
|
||||
keyRepeat?.Cancel();
|
||||
keyRepeat = this.BeginKeyRepeat(Scheduler, () => ActionRequested?.Invoke(e.Action), 150);
|
||||
return true;
|
||||
|
||||
case GlobalAction.ToggleMute:
|
||||
case GlobalAction.NextVolumeMeter:
|
||||
case GlobalAction.PreviousVolumeMeter:
|
||||
@ -41,7 +33,6 @@ namespace osu.Game.Overlays.Volume
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
keyRepeat?.Cancel();
|
||||
}
|
||||
|
||||
protected override bool OnScroll(ScrollEvent e)
|
||||
|
@ -4,6 +4,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -35,14 +37,24 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
this.beatmap = beatmap;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the difficulty of the beatmap with no mods applied.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A structure describing the difficulty of the beatmap.</returns>
|
||||
public DifficultyAttributes Calculate(CancellationToken cancellationToken = default)
|
||||
=> Calculate(Array.Empty<Mod>(), cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the difficulty of the beatmap using a specific mod combination.
|
||||
/// </summary>
|
||||
/// <param name="mods">The mods that should be applied to the beatmap.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A structure describing the difficulty of the beatmap.</returns>
|
||||
public DifficultyAttributes Calculate(params Mod[] mods)
|
||||
public DifficultyAttributes Calculate([NotNull] IEnumerable<Mod> mods, CancellationToken cancellationToken = default)
|
||||
{
|
||||
preProcess(mods);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
preProcess(mods, cancellationToken);
|
||||
|
||||
var skills = CreateSkills(Beatmap, playableMods, clockRate);
|
||||
|
||||
@ -52,20 +64,33 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
foreach (var hitObject in getDifficultyHitObjects())
|
||||
{
|
||||
foreach (var skill in skills)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
skill.ProcessInternal(hitObject);
|
||||
}
|
||||
}
|
||||
|
||||
return CreateDifficultyAttributes(Beatmap, playableMods, skills, clockRate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the difficulty of the beatmap and returns a set of <see cref="TimedDifficultyAttributes"/> representing the difficulty at every relevant time value in the beatmap.
|
||||
/// Calculates the difficulty of the beatmap with no mods applied and returns a set of <see cref="TimedDifficultyAttributes"/> representing the difficulty at every relevant time value in the beatmap.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The set of <see cref="TimedDifficultyAttributes"/>.</returns>
|
||||
public List<TimedDifficultyAttributes> CalculateTimed(CancellationToken cancellationToken = default)
|
||||
=> CalculateTimed(Array.Empty<Mod>(), cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the difficulty of the beatmap using a specific mod combination and returns a set of <see cref="TimedDifficultyAttributes"/> representing the difficulty at every relevant time value in the beatmap.
|
||||
/// </summary>
|
||||
/// <param name="mods">The mods that should be applied to the beatmap.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The set of <see cref="TimedDifficultyAttributes"/>.</returns>
|
||||
public List<TimedDifficultyAttributes> CalculateTimed(params Mod[] mods)
|
||||
public List<TimedDifficultyAttributes> CalculateTimed([NotNull] IEnumerable<Mod> mods, CancellationToken cancellationToken = default)
|
||||
{
|
||||
preProcess(mods);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
preProcess(mods, cancellationToken);
|
||||
|
||||
var attribs = new List<TimedDifficultyAttributes>();
|
||||
|
||||
@ -80,7 +105,10 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
progressiveBeatmap.HitObjects.Add(hitObject.BaseObject);
|
||||
|
||||
foreach (var skill in skills)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
skill.ProcessInternal(hitObject);
|
||||
}
|
||||
|
||||
attribs.Add(new TimedDifficultyAttributes(hitObject.EndTime * clockRate, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate)));
|
||||
}
|
||||
@ -99,7 +127,7 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
if (combination is MultiMod multi)
|
||||
yield return Calculate(multi.Mods);
|
||||
else
|
||||
yield return Calculate(combination);
|
||||
yield return Calculate(combination.Yield());
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,11 +140,12 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
/// Performs required tasks before every calculation.
|
||||
/// </summary>
|
||||
/// <param name="mods">The original list of <see cref="Mod"/>s.</param>
|
||||
private void preProcess(Mod[] mods)
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
private void preProcess([NotNull] IEnumerable<Mod> mods, CancellationToken cancellationToken = default)
|
||||
{
|
||||
playableMods = mods.Select(m => m.DeepClone()).ToArray();
|
||||
|
||||
Beatmap = beatmap.GetPlayableBeatmap(ruleset, playableMods);
|
||||
Beatmap = beatmap.GetPlayableBeatmap(ruleset, playableMods, cancellationToken);
|
||||
|
||||
var track = new TrackVirtual(10000);
|
||||
playableMods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(track));
|
||||
|
@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
/// <summary>
|
||||
/// Wraps a <see cref="DifficultyAttributes"/> object and adds a time value for which the attribute is valid.
|
||||
/// Output by <see cref="DifficultyCalculator.CalculateTimed"/>.
|
||||
/// Output by DifficultyCalculator.CalculateTimed methods.
|
||||
/// </summary>
|
||||
public class TimedDifficultyAttributes : IComparable<TimedDifficultyAttributes>
|
||||
{
|
||||
|
@ -168,6 +168,8 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer<T>
|
||||
{
|
||||
protected override bool HandleRepeats => false;
|
||||
|
||||
public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
: base(ruleset, variant, unique)
|
||||
{
|
||||
|
@ -14,7 +14,6 @@ using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -204,11 +203,11 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.IncreaseScrollSpeed:
|
||||
scheduleScrollSpeedAdjustment(1);
|
||||
AdjustScrollSpeed(1);
|
||||
return true;
|
||||
|
||||
case GlobalAction.DecreaseScrollSpeed:
|
||||
scheduleScrollSpeedAdjustment(-1);
|
||||
AdjustScrollSpeed(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -223,12 +222,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
scheduledScrollSpeedAdjustment = null;
|
||||
}
|
||||
|
||||
private void scheduleScrollSpeedAdjustment(int amount)
|
||||
{
|
||||
scheduledScrollSpeedAdjustment?.Cancel();
|
||||
scheduledScrollSpeedAdjustment = this.BeginKeyRepeat(Scheduler, () => AdjustScrollSpeed(amount));
|
||||
}
|
||||
|
||||
private class LocalScrollingInfo : IScrollingInfo
|
||||
{
|
||||
public IBindable<ScrollingDirection> Direction { get; } = new Bindable<ScrollingDirection>();
|
||||
|
@ -239,6 +239,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case PlatformAction.SelectAll:
|
||||
|
@ -393,6 +393,9 @@ namespace osu.Game.Screens.Edit
|
||||
return true;
|
||||
|
||||
case PlatformAction.Save:
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
Save();
|
||||
return true;
|
||||
}
|
||||
@ -457,6 +460,9 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Back:
|
||||
|
@ -220,6 +220,9 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Back:
|
||||
|
@ -21,6 +21,9 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
if (e.Action == GlobalAction.Back)
|
||||
{
|
||||
BeginConfirm();
|
||||
|
@ -12,7 +12,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Input.Bindings;
|
||||
@ -146,11 +145,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.SelectNext:
|
||||
beginRepeatSelection(() => selectNext(1), e.Action);
|
||||
selectNext(1);
|
||||
return true;
|
||||
|
||||
case GlobalAction.SelectPrevious:
|
||||
beginRepeatSelection(() => selectNext(-1), e.Action);
|
||||
selectNext(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -159,40 +158,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.SelectNext:
|
||||
case GlobalAction.SelectPrevious:
|
||||
endRepeatSelection(e.Action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private ScheduledDelegate repeatDelegate;
|
||||
private object lastRepeatSource;
|
||||
|
||||
/// <summary>
|
||||
/// Begin repeating the specified selection action.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to perform.</param>
|
||||
/// <param name="source">The source of the action. Used in conjunction with <see cref="endRepeatSelection"/> to only cancel the correct action (most recently pressed key).</param>
|
||||
private void beginRepeatSelection(Action action, object source)
|
||||
{
|
||||
endRepeatSelection();
|
||||
|
||||
lastRepeatSource = source;
|
||||
repeatDelegate = this.BeginKeyRepeat(Scheduler, action);
|
||||
}
|
||||
|
||||
private void endRepeatSelection(object source = null)
|
||||
{
|
||||
// only the most recent source should be able to cancel the current action.
|
||||
if (source != null && !EqualityComparer<object>.Default.Equals(lastRepeatSource, source))
|
||||
return;
|
||||
|
||||
repeatDelegate?.Cancel();
|
||||
repeatDelegate = null;
|
||||
lastRepeatSource = null;
|
||||
}
|
||||
|
||||
private void selectNext(int direction)
|
||||
|
@ -134,6 +134,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
if (SelectedRoom.Value != Room)
|
||||
return false;
|
||||
|
||||
|
@ -19,6 +19,9 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
if (!Enabled.Value)
|
||||
return false;
|
||||
|
||||
|
@ -65,6 +65,9 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Select:
|
||||
|
@ -208,6 +208,9 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Back:
|
||||
|
@ -216,7 +216,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
this.gameplayBeatmap = gameplayBeatmap;
|
||||
}
|
||||
|
||||
public override IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
|
||||
public override IBeatmap GetPlayableBeatmap(IRulesetInfo ruleset, IReadOnlyList<Mod> mods = null, CancellationToken? cancellationToken = null)
|
||||
=> gameplayBeatmap;
|
||||
|
||||
protected override IBeatmap GetBeatmap() => gameplayBeatmap;
|
||||
|
@ -283,6 +283,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.HoldForHUD:
|
||||
|
@ -12,6 +12,9 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
if (e.Action != GlobalAction.QuickExit) return false;
|
||||
|
||||
BeginConfirm();
|
||||
|
@ -12,6 +12,9 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
if (e.Action != GlobalAction.QuickRetry) return false;
|
||||
|
||||
BeginConfirm();
|
||||
|
@ -7,9 +7,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -48,8 +46,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false);
|
||||
|
||||
private ScheduledDelegate keyboardSeekDelegate;
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
const double keyboard_seek_amount = 5000;
|
||||
@ -57,13 +53,11 @@ namespace osu.Game.Screens.Play
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.SeekReplayBackward:
|
||||
keyboardSeekDelegate?.Cancel();
|
||||
keyboardSeekDelegate = this.BeginKeyRepeat(Scheduler, () => keyboardSeek(-1));
|
||||
keyboardSeek(-1);
|
||||
return true;
|
||||
|
||||
case GlobalAction.SeekReplayForward:
|
||||
keyboardSeekDelegate?.Cancel();
|
||||
keyboardSeekDelegate = this.BeginKeyRepeat(Scheduler, () => keyboardSeek(1));
|
||||
keyboardSeek(1);
|
||||
return true;
|
||||
|
||||
case GlobalAction.TogglePauseReplay:
|
||||
@ -86,13 +80,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.SeekReplayBackward:
|
||||
case GlobalAction.SeekReplayForward:
|
||||
keyboardSeekDelegate?.Cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,6 +146,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.SkipCutscene:
|
||||
|
@ -311,10 +311,10 @@ namespace osu.Game.Screens.Ranking
|
||||
ScorePanelList.Attach(detachedPanel);
|
||||
|
||||
// Move into its original location in the attached container first, then to the final location.
|
||||
var origLocation = detachedPanel.Parent.ToLocalSpace(screenSpacePos);
|
||||
detachedPanel.MoveTo(origLocation)
|
||||
float origLocation = detachedPanel.Parent.ToLocalSpace(screenSpacePos).X;
|
||||
detachedPanel.MoveToX(origLocation)
|
||||
.Then()
|
||||
.MoveTo(new Vector2(0, origLocation.Y), 150, Easing.OutQuint);
|
||||
.MoveToX(0, 150, Easing.OutQuint);
|
||||
|
||||
// Show contracted panels.
|
||||
foreach (var contracted in ScorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted))
|
||||
@ -330,6 +330,9 @@ namespace osu.Game.Screens.Ranking
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Select:
|
||||
|
@ -18,7 +18,6 @@ using osu.Framework.Threading;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Input.Bindings;
|
||||
@ -479,42 +478,27 @@ namespace osu.Game.Screens.Select
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Left:
|
||||
if (!e.Repeat)
|
||||
beginRepeatSelection(() => SelectNext(-1), e.Key);
|
||||
SelectNext(-1);
|
||||
return true;
|
||||
|
||||
case Key.Right:
|
||||
if (!e.Repeat)
|
||||
beginRepeatSelection(() => SelectNext(), e.Key);
|
||||
SelectNext();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnKeyUp(KeyUpEvent e)
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Left:
|
||||
case Key.Right:
|
||||
endRepeatSelection(e.Key);
|
||||
break;
|
||||
}
|
||||
|
||||
base.OnKeyUp(e);
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.SelectNext:
|
||||
beginRepeatSelection(() => SelectNext(1, false), e.Action);
|
||||
SelectNext(1, false);
|
||||
return true;
|
||||
|
||||
case GlobalAction.SelectPrevious:
|
||||
beginRepeatSelection(() => SelectNext(-1, false), e.Action);
|
||||
SelectNext(-1, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -523,40 +507,6 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.SelectNext:
|
||||
case GlobalAction.SelectPrevious:
|
||||
endRepeatSelection(e.Action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private ScheduledDelegate repeatDelegate;
|
||||
private object lastRepeatSource;
|
||||
|
||||
/// <summary>
|
||||
/// Begin repeating the specified selection action.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to perform.</param>
|
||||
/// <param name="source">The source of the action. Used in conjunction with <see cref="endRepeatSelection"/> to only cancel the correct action (most recently pressed key).</param>
|
||||
private void beginRepeatSelection(Action action, object source)
|
||||
{
|
||||
endRepeatSelection();
|
||||
|
||||
lastRepeatSource = source;
|
||||
repeatDelegate = this.BeginKeyRepeat(Scheduler, action);
|
||||
}
|
||||
|
||||
private void endRepeatSelection(object source = null)
|
||||
{
|
||||
// only the most recent source should be able to cancel the current action.
|
||||
if (source != null && !EqualityComparer<object>.Default.Equals(lastRepeatSource, source))
|
||||
return;
|
||||
|
||||
repeatDelegate?.Cancel();
|
||||
repeatDelegate = null;
|
||||
lastRepeatSource = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -821,6 +821,9 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
if (!this.IsCurrentScreen()) return false;
|
||||
|
||||
switch (e.Action)
|
||||
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.6.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1108.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1118.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1112.0" />
|
||||
<PackageReference Include="Sentry" Version="3.10.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.0" />
|
||||
|
@ -70,7 +70,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.1108.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.1118.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1112.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||
@ -93,7 +93,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1108.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.1118.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user