1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-20 03:47:28 +08:00

Merge branch 'master' into improve-download-limit-message

This commit is contained in:
Dean Herbert 2022-08-03 20:17:02 +09:00 committed by GitHub
commit 87cc2db624
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 304 additions and 141 deletions

View File

@ -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)
{

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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()
{
}
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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)
{

View File

@ -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:

View File

@ -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;

View File

@ -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()
{

View File

@ -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()
{

View File

@ -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]

View File

@ -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;

View File

@ -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;

View File

@ -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>

View File

@ -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

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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)

View File

@ -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)
{

View File

@ -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 };
}
}

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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; }

View File

@ -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]

View File

@ -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;
}

View File

@ -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;
}