mirror of
https://github.com/ppy/osu.git
synced 2025-03-20 05:07:19 +08:00
Merge branch 'master' into improve-download-limit-message
This commit is contained in:
commit
87cc2db624
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
@ -36,9 +35,9 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
|
||||
public override float DefaultFlashlightSize => 350;
|
||||
|
||||
protected override Flashlight CreateFlashlight() => new CatchFlashlight(this, playfield.AsNonNull());
|
||||
protected override Flashlight CreateFlashlight() => new CatchFlashlight(this, playfield);
|
||||
|
||||
private CatchPlayfield? playfield;
|
||||
private CatchPlayfield playfield = null!;
|
||||
|
||||
public override void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
|
||||
{
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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.Diagnostics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
@ -19,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public override string Description => @"Use the mouse to control the catcher.";
|
||||
|
||||
private DrawableRuleset<CatchHitObject>? drawableRuleset;
|
||||
private DrawableRuleset<CatchHitObject> drawableRuleset = null!;
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
|
||||
{
|
||||
@ -28,8 +27,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
|
||||
public void ApplyToPlayer(Player player)
|
||||
{
|
||||
Debug.Assert(drawableRuleset != null);
|
||||
|
||||
if (!drawableRuleset.HasReplayLoaded.Value)
|
||||
drawableRuleset.Cursor.Add(new MouseInputHelper((CatchPlayfield)drawableRuleset.Playfield));
|
||||
}
|
||||
|
@ -2,8 +2,10 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@ -22,8 +24,15 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
||||
|
||||
private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles
|
||||
private const int wiggle_strength = 10; // Higher = stronger wiggles
|
||||
private const int wiggle_duration = 100; // (ms) Higher = fewer wiggles
|
||||
|
||||
[SettingSource("Strength", "Multiplier applied to the wiggling strength.")]
|
||||
public BindableDouble Strength { get; } = new BindableDouble(1)
|
||||
{
|
||||
MinValue = 0.1f,
|
||||
MaxValue = 2f,
|
||||
Precision = 0.1f
|
||||
};
|
||||
|
||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => drawableOnApplyCustomUpdateState(hitObject, state);
|
||||
|
||||
@ -47,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
void wiggle()
|
||||
{
|
||||
float nextAngle = (float)(objRand.NextDouble() * 2 * Math.PI);
|
||||
float nextDist = (float)(objRand.NextDouble() * wiggle_strength);
|
||||
float nextDist = (float)(objRand.NextDouble() * Strength.Value * 7);
|
||||
drawable.MoveTo(new Vector2((float)(nextDist * Math.Cos(nextAngle) + origin.X), (float)(nextDist * Math.Sin(nextAngle) + origin.Y)), wiggle_duration);
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.Diagnostics;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -32,37 +30,42 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnTrackingChanged(ValueChangedEvent<bool> tracking)
|
||||
protected override void OnSliderPress()
|
||||
{
|
||||
Debug.Assert(ParentObject != null);
|
||||
|
||||
const float duration = 300f;
|
||||
|
||||
if (ParentObject.Judged)
|
||||
return;
|
||||
if (Precision.AlmostEquals(0, Alpha))
|
||||
this.ScaleTo(1);
|
||||
|
||||
if (tracking.NewValue)
|
||||
{
|
||||
if (Precision.AlmostEquals(0, Alpha))
|
||||
this.ScaleTo(1);
|
||||
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA, duration, Easing.OutQuint)
|
||||
.FadeIn(duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA, duration, Easing.OutQuint)
|
||||
.FadeTo(1f, duration, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.2f, duration / 2, Easing.OutQuint)
|
||||
.FadeTo(0, duration / 2, Easing.OutQuint);
|
||||
}
|
||||
protected override void OnSliderRelease()
|
||||
{
|
||||
const float duration = 150;
|
||||
|
||||
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.2f, duration, Easing.OutQuint)
|
||||
.FadeTo(0, duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void OnSliderEnd()
|
||||
{
|
||||
const float fade_duration = 300;
|
||||
const float duration = 300;
|
||||
|
||||
// intentionally pile on an extra FadeOut to make it happen much faster
|
||||
this.ScaleTo(1, fade_duration, Easing.OutQuint);
|
||||
this.FadeOut(fade_duration / 2, Easing.OutQuint);
|
||||
this.ScaleTo(1, duration, Easing.OutQuint)
|
||||
.FadeOut(duration / 2, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void OnSliderTick()
|
||||
{
|
||||
this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.08f, 40, Easing.OutQuint)
|
||||
.Then()
|
||||
.ScaleTo(DrawableSliderBall.FOLLOW_AREA, 200f, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void OnSliderBreak()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +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.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -23,7 +23,17 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
((DrawableSlider?)ParentObject)?.Tracking.BindValueChanged(OnTrackingChanged, true);
|
||||
((DrawableSlider?)ParentObject)?.Tracking.BindValueChanged(tracking =>
|
||||
{
|
||||
Debug.Assert(ParentObject != null);
|
||||
if (ParentObject.Judged)
|
||||
return;
|
||||
|
||||
if (tracking.NewValue)
|
||||
OnSliderPress();
|
||||
else
|
||||
OnSliderRelease();
|
||||
}, true);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -48,13 +58,46 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
|
||||
private void updateStateTransforms(DrawableHitObject drawableObject, ArmedState state)
|
||||
{
|
||||
// Gets called by slider ticks, tails, etc., leading to duplicated
|
||||
// animations which may negatively affect performance
|
||||
if (drawableObject is not DrawableSlider)
|
||||
return;
|
||||
Debug.Assert(ParentObject != null);
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
OnSliderEnd();
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
switch (drawableObject)
|
||||
{
|
||||
case DrawableSliderTail:
|
||||
// Use ParentObject instead of drawableObject because slider tail's
|
||||
// HitStateUpdateTime is ~36ms before the actual slider end (aka slider
|
||||
// tail leniency)
|
||||
using (BeginAbsoluteSequence(ParentObject.HitStateUpdateTime))
|
||||
OnSliderEnd();
|
||||
break;
|
||||
|
||||
case DrawableSliderTick:
|
||||
case DrawableSliderRepeat:
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
OnSliderTick();
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ArmedState.Miss:
|
||||
switch (drawableObject)
|
||||
{
|
||||
case DrawableSliderTail:
|
||||
case DrawableSliderTick:
|
||||
case DrawableSliderRepeat:
|
||||
// Despite above comment, ok to use drawableObject.HitStateUpdateTime
|
||||
// here, since on stable, the break anim plays right when the tail is
|
||||
// missed, not when the slider ends
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
OnSliderBreak();
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
@ -68,8 +111,14 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void OnTrackingChanged(ValueChangedEvent<bool> tracking);
|
||||
protected abstract void OnSliderPress();
|
||||
|
||||
protected abstract void OnSliderRelease();
|
||||
|
||||
protected abstract void OnSliderEnd();
|
||||
|
||||
protected abstract void OnSliderTick();
|
||||
|
||||
protected abstract void OnSliderBreak();
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
@ -21,29 +20,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
InternalChild = animationContent;
|
||||
}
|
||||
|
||||
protected override void OnTrackingChanged(ValueChangedEvent<bool> tracking)
|
||||
protected override void OnSliderPress()
|
||||
{
|
||||
Debug.Assert(ParentObject != null);
|
||||
|
||||
if (ParentObject.Judged)
|
||||
return;
|
||||
|
||||
double remainingTime = Math.Max(0, ParentObject.HitStateUpdateTime - Time.Current);
|
||||
|
||||
// Note that the scale adjust here is 2 instead of DrawableSliderBall.FOLLOW_AREA to match legacy behaviour.
|
||||
// This means the actual tracking area for gameplay purposes is larger than the sprite (but skins may be accounting for this).
|
||||
if (tracking.NewValue)
|
||||
{
|
||||
// TODO: Follow circle should bounce on each slider tick.
|
||||
this.ScaleTo(0.5f).ScaleTo(2f, Math.Min(180f, remainingTime), Easing.Out)
|
||||
.FadeTo(0).FadeTo(1f, Math.Min(60f, remainingTime));
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Should animate only at the next slider tick if we want to match stable perfectly.
|
||||
this.ScaleTo(4f, 100)
|
||||
.FadeTo(0f, 100);
|
||||
}
|
||||
this.ScaleTo(0.5f).ScaleTo(2f, Math.Min(180f, remainingTime), Easing.Out)
|
||||
.FadeTo(0).FadeTo(1f, Math.Min(60f, remainingTime));
|
||||
}
|
||||
|
||||
protected override void OnSliderRelease()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnSliderEnd()
|
||||
@ -51,5 +41,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
this.ScaleTo(1.6f, 200, Easing.Out)
|
||||
.FadeOut(200, Easing.In);
|
||||
}
|
||||
|
||||
protected override void OnSliderTick()
|
||||
{
|
||||
this.ScaleTo(2.2f)
|
||||
.ScaleTo(2f, 200);
|
||||
}
|
||||
|
||||
protected override void OnSliderBreak()
|
||||
{
|
||||
this.ScaleTo(4f, 100)
|
||||
.FadeTo(0f, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Layout;
|
||||
using osu.Game.Configuration;
|
||||
@ -37,9 +36,9 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
|
||||
public override float DefaultFlashlightSize => 250;
|
||||
|
||||
protected override Flashlight CreateFlashlight() => new TaikoFlashlight(this, playfield.AsNonNull());
|
||||
protected override Flashlight CreateFlashlight() => new TaikoFlashlight(this, playfield);
|
||||
|
||||
private TaikoPlayfield? playfield;
|
||||
private TaikoPlayfield playfield = null!;
|
||||
|
||||
public override void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
||||
{
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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.Diagnostics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -31,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
/// </summary>
|
||||
private const float fade_out_duration = 0.375f;
|
||||
|
||||
private DrawableTaikoRuleset? drawableRuleset;
|
||||
private DrawableTaikoRuleset drawableRuleset = null!;
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
||||
{
|
||||
@ -45,8 +44,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
|
||||
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
Debug.Assert(drawableRuleset != null);
|
||||
|
||||
switch (hitObject)
|
||||
{
|
||||
case DrawableDrumRollTick:
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Audio;
|
||||
|
||||
|
@ -486,9 +486,6 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddAssert("Something is selected", () => carousel.SelectedBeatmapInfo != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test sorting
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSorting()
|
||||
{
|
||||
@ -517,6 +514,9 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddAssert($"Check {zzz_string} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Artist == zzz_string);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures stability is maintained on different sort modes for items with equal properties.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSortingStability()
|
||||
{
|
||||
@ -549,6 +549,53 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddAssert("Items reset to original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == idOffset + index).All(b => b));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures stability is maintained on different sort modes while a new item is added to the carousel.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSortingStabilityWithNewItems()
|
||||
{
|
||||
List<BeatmapSetInfo> sets = new List<BeatmapSetInfo>();
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(3);
|
||||
|
||||
// only need to set the first as they are a shared reference.
|
||||
var beatmap = set.Beatmaps.First();
|
||||
|
||||
beatmap.Metadata.Artist = "same artist";
|
||||
beatmap.Metadata.Title = "same title";
|
||||
|
||||
sets.Add(set);
|
||||
}
|
||||
|
||||
int idOffset = sets.First().OnlineID;
|
||||
|
||||
loadBeatmaps(sets);
|
||||
|
||||
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
|
||||
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == idOffset + index).All(b => b));
|
||||
|
||||
AddStep("Add new item", () =>
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo();
|
||||
|
||||
// only need to set the first as they are a shared reference.
|
||||
var beatmap = set.Beatmaps.First();
|
||||
|
||||
beatmap.Metadata.Artist = "same artist";
|
||||
beatmap.Metadata.Title = "same title";
|
||||
|
||||
carousel.UpdateBeatmapSet(set);
|
||||
});
|
||||
|
||||
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == idOffset + index).All(b => b));
|
||||
|
||||
AddStep("Sort by title", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false));
|
||||
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == idOffset + index).All(b => b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSortingWithFiltered()
|
||||
{
|
||||
|
@ -10,6 +10,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
@ -282,6 +283,28 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddAssert("filter count is 2", () => songSelect.FilterCount == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCarouselSelectionUpdatesOnResume()
|
||||
{
|
||||
addRulesetImportStep(0);
|
||||
|
||||
createSongSelect();
|
||||
|
||||
AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child")));
|
||||
AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
|
||||
|
||||
AddStep("update beatmap", () =>
|
||||
{
|
||||
var selectedBeatmap = Beatmap.Value.BeatmapInfo;
|
||||
var anotherBeatmap = Beatmap.Value.BeatmapSetInfo.Beatmaps.Except(selectedBeatmap.Yield()).First();
|
||||
Beatmap.Value = manager.GetWorkingBeatmap(anotherBeatmap);
|
||||
});
|
||||
|
||||
AddStep("return", () => songSelect.MakeCurrent());
|
||||
AddUntilStep("wait for current", () => songSelect.IsCurrentScreen());
|
||||
AddAssert("carousel updated", () => songSelect.Carousel.SelectedBeatmapInfo.Equals(Beatmap.Value.BeatmapInfo));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAudioResuming()
|
||||
{
|
||||
|
@ -3,9 +3,13 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK.Input;
|
||||
@ -99,7 +103,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
Origin = Anchor.Centre,
|
||||
Text = "Fixed width"
|
||||
});
|
||||
AddAssert("draw width is 200", () => toggleButton.DrawWidth, () => Is.EqualTo(200).Within(Precision.FLOAT_EPSILON));
|
||||
|
||||
AddStep("change text", () => toggleButton.Text = "New text");
|
||||
AddAssert("draw width is 200", () => toggleButton.DrawWidth, () => Is.EqualTo(200).Within(Precision.FLOAT_EPSILON));
|
||||
|
||||
AddStep("create auto-sizing button", () => Child = toggleButton = new ShearedToggleButton
|
||||
{
|
||||
@ -107,7 +114,14 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
Origin = Anchor.Centre,
|
||||
Text = "This button autosizes to its text!"
|
||||
});
|
||||
AddAssert("button is wider than text", () => toggleButton.DrawWidth, () => Is.GreaterThan(toggleButton.ChildrenOfType<SpriteText>().Single().DrawWidth));
|
||||
|
||||
float originalDrawWidth = 0;
|
||||
AddStep("store button width", () => originalDrawWidth = toggleButton.DrawWidth);
|
||||
|
||||
AddStep("change text", () => toggleButton.Text = "New text");
|
||||
AddAssert("button is wider than text", () => toggleButton.DrawWidth, () => Is.GreaterThan(toggleButton.ChildrenOfType<SpriteText>().Single().DrawWidth));
|
||||
AddAssert("button width decreased", () => toggleButton.DrawWidth, () => Is.LessThan(originalDrawWidth));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.Diagnostics;
|
||||
using ManagedBass.Fx;
|
||||
using osu.Framework.Audio.Mixing;
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.Transforms;
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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
|
||||
|
||||
namespace osu.Game.Audio
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Audio
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Skinning;
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
@ -18,15 +16,15 @@ namespace osu.Game.Audio
|
||||
/// Invoked when this <see cref="PreviewTrack"/> has stopped playing.
|
||||
/// Not invoked in a thread-safe context.
|
||||
/// </summary>
|
||||
public event Action Stopped;
|
||||
public event Action? Stopped;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when this <see cref="PreviewTrack"/> has started playing.
|
||||
/// Not invoked in a thread-safe context.
|
||||
/// </summary>
|
||||
public event Action Started;
|
||||
public event Action? Started;
|
||||
|
||||
protected Track Track { get; private set; }
|
||||
protected Track? Track { get; private set; }
|
||||
|
||||
private bool hasStarted;
|
||||
|
||||
@ -58,7 +56,7 @@ namespace osu.Game.Audio
|
||||
/// </summary>
|
||||
public bool IsRunning => Track?.IsRunning ?? false;
|
||||
|
||||
private ScheduledDelegate startDelegate;
|
||||
private ScheduledDelegate? startDelegate;
|
||||
|
||||
/// <summary>
|
||||
/// Starts playing this <see cref="PreviewTrack"/>.
|
||||
@ -106,6 +104,6 @@ namespace osu.Game.Audio
|
||||
/// <summary>
|
||||
/// Retrieves the audio track.
|
||||
/// </summary>
|
||||
protected abstract Track GetTrack();
|
||||
protected abstract Track? GetTrack();
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
@ -20,9 +18,9 @@ namespace osu.Game.Audio
|
||||
|
||||
private readonly BindableDouble muteBindable = new BindableDouble();
|
||||
|
||||
private ITrackStore trackStore;
|
||||
private ITrackStore trackStore = null!;
|
||||
|
||||
protected TrackManagerPreviewTrack CurrentTrack;
|
||||
protected TrackManagerPreviewTrack? CurrentTrack;
|
||||
|
||||
public PreviewTrackManager(IAdjustableAudioComponent mainTrackAdjustments)
|
||||
{
|
||||
@ -89,8 +87,8 @@ namespace osu.Game.Audio
|
||||
|
||||
public class TrackManagerPreviewTrack : PreviewTrack
|
||||
{
|
||||
[Resolved(canBeNull: true)]
|
||||
public IPreviewTrackOwner Owner { get; private set; }
|
||||
[Resolved]
|
||||
public IPreviewTrackOwner? Owner { get; private set; }
|
||||
|
||||
private readonly IBeatmapSetInfo beatmapSetInfo;
|
||||
private readonly ITrackStore trackManager;
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
@ -34,7 +32,7 @@ namespace osu.Game.Audio
|
||||
Volume);
|
||||
}
|
||||
|
||||
public bool Equals(SampleInfo other)
|
||||
public bool Equals(SampleInfo? other)
|
||||
=> other != null && sampleNames.SequenceEqual(other.sampleNames);
|
||||
|
||||
public override bool Equals(object obj)
|
||||
|
@ -21,6 +21,11 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
private bool allowImmediateFocus => host?.OnScreenKeyboardOverlapsGameWindow != true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the content of the text box should be cleared on the first "back" key press.
|
||||
/// </summary>
|
||||
protected virtual bool ClearTextOnBackKey => true;
|
||||
|
||||
public void TakeFocus()
|
||||
{
|
||||
if (!allowImmediateFocus)
|
||||
@ -78,7 +83,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
if (!HasFocus) return false;
|
||||
|
||||
if (e.Action == GlobalAction.Back)
|
||||
if (ClearTextOnBackKey && e.Action == GlobalAction.Back)
|
||||
{
|
||||
if (Text.Length > 0)
|
||||
{
|
||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
backgroundLayer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
CornerRadius = corner_radius,
|
||||
Masking = true,
|
||||
BorderThickness = 2,
|
||||
@ -128,10 +128,12 @@ namespace osu.Game.Graphics.UserInterface
|
||||
if (width != null)
|
||||
{
|
||||
Width = width.Value;
|
||||
backgroundLayer.RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoSizeAxes = Axes.X;
|
||||
backgroundLayer.AutoSizeAxes = Axes.X;
|
||||
text.Margin = new MarginPadding { Horizontal = 15 };
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,11 @@ namespace osu.Game.IO
|
||||
/// </summary>
|
||||
public virtual string[] IgnoreFiles => Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// A list of file/directory suffixes which should not be migrated.
|
||||
/// </summary>
|
||||
public virtual string[] IgnoreSuffixes => Array.Empty<string>();
|
||||
|
||||
protected MigratableStorage(Storage storage, string subPath = null)
|
||||
: base(storage, subPath)
|
||||
{
|
||||
@ -73,6 +78,9 @@ namespace osu.Game.IO
|
||||
if (topLevelExcludes && IgnoreFiles.Contains(fi.Name))
|
||||
continue;
|
||||
|
||||
if (IgnoreSuffixes.Any(suffix => fi.Name.EndsWith(suffix, StringComparison.Ordinal)))
|
||||
continue;
|
||||
|
||||
allFilesDeleted &= AttemptOperation(() => fi.Delete(), throwOnFailure: false);
|
||||
}
|
||||
|
||||
@ -81,6 +89,9 @@ namespace osu.Game.IO
|
||||
if (topLevelExcludes && IgnoreDirectories.Contains(dir.Name))
|
||||
continue;
|
||||
|
||||
if (IgnoreSuffixes.Any(suffix => dir.Name.EndsWith(suffix, StringComparison.Ordinal)))
|
||||
continue;
|
||||
|
||||
allFilesDeleted &= AttemptOperation(() => dir.Delete(true), throwOnFailure: false);
|
||||
}
|
||||
|
||||
@ -101,6 +112,9 @@ namespace osu.Game.IO
|
||||
if (topLevelExcludes && IgnoreFiles.Contains(fileInfo.Name))
|
||||
continue;
|
||||
|
||||
if (IgnoreSuffixes.Any(suffix => fileInfo.Name.EndsWith(suffix, StringComparison.Ordinal)))
|
||||
continue;
|
||||
|
||||
AttemptOperation(() =>
|
||||
{
|
||||
fileInfo.Refresh();
|
||||
@ -119,6 +133,9 @@ namespace osu.Game.IO
|
||||
if (topLevelExcludes && IgnoreDirectories.Contains(dir.Name))
|
||||
continue;
|
||||
|
||||
if (IgnoreSuffixes.Any(suffix => dir.Name.EndsWith(suffix, StringComparison.Ordinal)))
|
||||
continue;
|
||||
|
||||
CopyRecursive(dir, destination.CreateSubdirectory(dir.Name), false);
|
||||
}
|
||||
}
|
||||
|
@ -38,15 +38,20 @@ namespace osu.Game.IO
|
||||
public override string[] IgnoreDirectories => new[]
|
||||
{
|
||||
"cache",
|
||||
$"{OsuGameBase.CLIENT_DATABASE_FILENAME}.management",
|
||||
};
|
||||
|
||||
public override string[] IgnoreFiles => new[]
|
||||
{
|
||||
"framework.ini",
|
||||
"storage.ini",
|
||||
$"{OsuGameBase.CLIENT_DATABASE_FILENAME}.note",
|
||||
$"{OsuGameBase.CLIENT_DATABASE_FILENAME}.lock",
|
||||
};
|
||||
|
||||
public override string[] IgnoreSuffixes => new[]
|
||||
{
|
||||
// Realm pipe files don't play well with copy operations
|
||||
".note",
|
||||
".lock",
|
||||
".management",
|
||||
};
|
||||
|
||||
public OsuStorage(GameHost host, Storage defaultStorage)
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
protected override MultipleSelectionFilterTabItem CreateTabItem(ScoreRank value) => new RankItem(value);
|
||||
|
||||
protected override IEnumerable<ScoreRank> GetValues() => base.GetValues().Reverse();
|
||||
protected override IEnumerable<ScoreRank> GetValues() => base.GetValues().Where(r => r > ScoreRank.F).Reverse();
|
||||
}
|
||||
|
||||
private class RankItem : MultipleSelectionFilterTabItem
|
||||
|
@ -13,6 +13,8 @@ namespace osu.Game.Overlays.Chat
|
||||
|
||||
public override bool HandleLeftRightArrows => !ShowSearch.Value;
|
||||
|
||||
protected override bool ClearTextOnBackKey => false;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.Specialized;
|
||||
using System.Linq;
|
||||
@ -29,9 +27,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
public class PlaylistsRoomSettingsOverlay : RoomSettingsOverlay
|
||||
{
|
||||
public Action EditPlaylist;
|
||||
public Action? EditPlaylist;
|
||||
|
||||
private MatchSettings settings;
|
||||
private MatchSettings settings = null!;
|
||||
|
||||
protected override OsuButton SubmitButton => settings.ApplyButton;
|
||||
|
||||
@ -55,28 +53,30 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
private const float disabled_alpha = 0.2f;
|
||||
|
||||
public Action EditPlaylist;
|
||||
public Action? EditPlaylist;
|
||||
|
||||
public OsuTextBox NameField, MaxParticipantsField, MaxAttemptsField;
|
||||
public OsuDropdown<TimeSpan> DurationField;
|
||||
public RoomAvailabilityPicker AvailabilityPicker;
|
||||
public TriangleButton ApplyButton;
|
||||
public OsuTextBox NameField = null!, MaxParticipantsField = null!, MaxAttemptsField = null!;
|
||||
public OsuDropdown<TimeSpan> DurationField = null!;
|
||||
public RoomAvailabilityPicker AvailabilityPicker = null!;
|
||||
public TriangleButton ApplyButton = null!;
|
||||
|
||||
public bool IsLoading => loadingLayer.State.Value == Visibility.Visible;
|
||||
|
||||
public OsuSpriteText ErrorText;
|
||||
public OsuSpriteText ErrorText = null!;
|
||||
|
||||
private LoadingLayer loadingLayer;
|
||||
private DrawableRoomPlaylist playlist;
|
||||
private OsuSpriteText playlistLength;
|
||||
private LoadingLayer loadingLayer = null!;
|
||||
private DrawableRoomPlaylist playlist = null!;
|
||||
private OsuSpriteText playlistLength = null!;
|
||||
|
||||
private PurpleTriangleButton editPlaylistButton;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IRoomManager manager { get; set; }
|
||||
private PurpleTriangleButton editPlaylistButton = null!;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
private IRoomManager? manager { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
private IBindable<APIUser> localUser = null!;
|
||||
|
||||
private readonly Room room;
|
||||
|
||||
@ -304,7 +304,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
MaxAttempts.BindValueChanged(count => MaxAttemptsField.Text = count.NewValue?.ToString(), true);
|
||||
Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true);
|
||||
|
||||
api.LocalUser.BindValueChanged(populateDurations, true);
|
||||
localUser = api.LocalUser.GetBoundCopy();
|
||||
localUser.BindValueChanged(populateDurations, true);
|
||||
|
||||
playlist.Items.BindTo(Playlist);
|
||||
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
||||
|
@ -40,8 +40,6 @@ namespace osu.Game.Screens.Play.HUD
|
||||
public override bool HandleNonPositionalInput => AllowSeeking.Value;
|
||||
public override bool HandlePositionalInput => AllowSeeking.Value;
|
||||
|
||||
protected override bool BlockScrollInput => false;
|
||||
|
||||
[Resolved]
|
||||
private Player? player { get; set; }
|
||||
|
||||
|
@ -14,6 +14,12 @@ namespace osu.Game.Screens.Play.HUD
|
||||
{
|
||||
public abstract class SongProgress : OverlayContainer, ISkinnableDrawable
|
||||
{
|
||||
// Some implementations of this element allow seeking during gameplay playback.
|
||||
// Set a sane default of never handling input to override the behaviour provided by OverlayContainer.
|
||||
public override bool HandleNonPositionalInput => false;
|
||||
public override bool HandlePositionalInput => false;
|
||||
protected override bool BlockScrollInput => false;
|
||||
|
||||
public bool UsesFixedAnchor { get; set; }
|
||||
|
||||
[Resolved]
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Screens.Select.Carousel
|
||||
{
|
||||
@ -15,7 +14,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
public IReadOnlyList<CarouselItem> Items => items;
|
||||
|
||||
private List<CarouselItem> items = new List<CarouselItem>();
|
||||
private readonly List<CarouselItem> items = new List<CarouselItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Used to assign a monotonically increasing ID to items as they are added. This member is
|
||||
@ -24,9 +23,6 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
private ulong currentItemID;
|
||||
|
||||
private Comparer<CarouselItem>? criteriaComparer;
|
||||
|
||||
private static readonly Comparer<CarouselItem> item_id_comparer = Comparer<CarouselItem>.Create((x, y) => x.ItemID.CompareTo(y.ItemID));
|
||||
|
||||
private FilterCriteria? lastCriteria;
|
||||
|
||||
protected int GetIndexOfItem(CarouselItem lastSelected) => items.IndexOf(lastSelected);
|
||||
@ -90,9 +86,16 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
items.ForEach(c => c.Filter(criteria));
|
||||
|
||||
// IEnumerable<T>.OrderBy() is used instead of List<T>.Sort() to ensure sorting stability
|
||||
criteriaComparer = Comparer<CarouselItem>.Create((x, y) => x.CompareTo(criteria, y));
|
||||
items = items.OrderBy(c => c, criteriaComparer).ThenBy(c => c, item_id_comparer).ToList();
|
||||
criteriaComparer = Comparer<CarouselItem>.Create((x, y) =>
|
||||
{
|
||||
int comparison = x.CompareTo(criteria, y);
|
||||
if (comparison != 0)
|
||||
return comparison;
|
||||
|
||||
return x.ItemID.CompareTo(y.ItemID);
|
||||
});
|
||||
|
||||
items.Sort(criteriaComparer);
|
||||
|
||||
lastCriteria = criteria;
|
||||
}
|
||||
|
@ -405,20 +405,21 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private ScheduledDelegate selectionChangedDebounce;
|
||||
|
||||
private void workingBeatmapChanged(ValueChangedEvent<WorkingBeatmap> e)
|
||||
private void updateCarouselSelection(ValueChangedEvent<WorkingBeatmap> e = null)
|
||||
{
|
||||
if (e.NewValue is DummyWorkingBeatmap || !this.IsCurrentScreen()) return;
|
||||
var beatmap = e?.NewValue ?? Beatmap.Value;
|
||||
if (beatmap is DummyWorkingBeatmap || !this.IsCurrentScreen()) return;
|
||||
|
||||
Logger.Log($"Song select working beatmap updated to {e.NewValue}");
|
||||
Logger.Log($"Song select working beatmap updated to {beatmap}");
|
||||
|
||||
if (!Carousel.SelectBeatmap(e.NewValue.BeatmapInfo, false))
|
||||
if (!Carousel.SelectBeatmap(beatmap.BeatmapInfo, false))
|
||||
{
|
||||
// A selection may not have been possible with filters applied.
|
||||
|
||||
// There was possibly a ruleset mismatch. This is a case we can help things along by updating the game-wide ruleset to match.
|
||||
if (!e.NewValue.BeatmapInfo.Ruleset.Equals(decoupledRuleset.Value))
|
||||
if (!beatmap.BeatmapInfo.Ruleset.Equals(decoupledRuleset.Value))
|
||||
{
|
||||
Ruleset.Value = e.NewValue.BeatmapInfo.Ruleset;
|
||||
Ruleset.Value = beatmap.BeatmapInfo.Ruleset;
|
||||
transferRulesetValue();
|
||||
}
|
||||
|
||||
@ -426,10 +427,10 @@ namespace osu.Game.Screens.Select
|
||||
// we still want to temporarily show the new beatmap, bypassing filters.
|
||||
// This will be undone the next time the user changes the filter.
|
||||
var criteria = FilterControl.CreateCriteria();
|
||||
criteria.SelectedBeatmapSet = e.NewValue.BeatmapInfo.BeatmapSet;
|
||||
criteria.SelectedBeatmapSet = beatmap.BeatmapInfo.BeatmapSet;
|
||||
Carousel.Filter(criteria);
|
||||
|
||||
Carousel.SelectBeatmap(e.NewValue.BeatmapInfo);
|
||||
Carousel.SelectBeatmap(beatmap.BeatmapInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,6 +598,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
|
||||
{
|
||||
updateCarouselSelection();
|
||||
|
||||
updateComponentFromBeatmap(Beatmap.Value);
|
||||
|
||||
if (ControlGlobalMusic)
|
||||
@ -805,7 +808,7 @@ namespace osu.Game.Screens.Select
|
||||
};
|
||||
decoupledRuleset.DisabledChanged += r => Ruleset.Disabled = r;
|
||||
|
||||
Beatmap.BindValueChanged(workingBeatmapChanged);
|
||||
Beatmap.BindValueChanged(updateCarouselSelection);
|
||||
|
||||
boundLocalBindables = true;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user