diff --git a/osu.Android.props b/osu.Android.props
index 2d3bfaf7ce..62397ca028 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,7 +51,7 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs
index 8c48158acd..466cbdaf8d 100644
--- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs
@@ -14,6 +14,7 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
+ [Timeout(10000)]
public class CatchBeatmapConversionTest : BeatmapConversionTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs
index 18cc300ff9..f009c10a9c 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
@@ -23,19 +22,19 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{
Name = @"Fruit Count",
Content = fruits.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles),
},
new BeatmapStatistic
{
Name = @"Juice Stream Count",
Content = juiceStreams.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders),
},
new BeatmapStatistic
{
Name = @"Banana Shower Count",
Content = bananaShowers.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners),
}
};
}
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index 9437023c70..ca75a816f1 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -21,13 +21,11 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using System;
-using osu.Framework.Testing;
using osu.Game.Rulesets.Catch.Skinning;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch
{
- [ExcludeFromDynamicCompile]
public class CatchRuleset : Ruleset, ILegacyRuleset
{
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableCatchRuleset(this, beatmap, mods);
diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs
index d0ff1fab43..0c57267970 100644
--- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs
@@ -14,11 +14,13 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
+ [Timeout(10000)]
public class ManiaBeatmapConversionTest : BeatmapConversionTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
[TestCase("basic")]
+ [TestCase("zero-length-slider")]
public void Test(string name) => base.Test(name);
protected override IEnumerable CreateConvertValue(HitObject hitObject)
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs
index dc24a344e9..d1d5adea75 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
@@ -41,14 +40,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
new BeatmapStatistic
{
Name = @"Note Count",
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles),
Content = notes.ToString(),
- Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Hold Note Count",
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders),
Content = holdnotes.ToString(),
- Icon = FontAwesome.Regular.Circle
},
};
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index b025ac7992..211905835c 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -5,7 +5,6 @@ using osu.Game.Rulesets.Mania.Objects;
using System;
using System.Linq;
using System.Collections.Generic;
-using osu.Framework.Utils;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
@@ -167,8 +166,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
var positionData = original as IHasPosition;
- for (double time = original.StartTime; !Precision.DefinitelyBigger(time, generator.EndTime); time += generator.SegmentDuration)
+ for (int i = 0; i <= generator.SpanCount; i++)
{
+ double time = original.StartTime + generator.SegmentDuration * i;
+
recordNote(time, positionData?.Position ?? Vector2.Zero);
computeDensity(time);
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index d03eb0b3c9..fe146c5324 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -27,8 +27,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
public readonly double EndTime;
public readonly double SegmentDuration;
-
- private readonly int spanCount;
+ public readonly int SpanCount;
private PatternType convertType;
@@ -42,20 +41,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var distanceData = hitObject as IHasDistance;
var repeatsData = hitObject as IHasRepeats;
- spanCount = repeatsData?.SpanCount() ?? 1;
+ SpanCount = repeatsData?.SpanCount() ?? 1;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime);
// The true distance, accounting for any repeats
- double distance = (distanceData?.Distance ?? 0) * spanCount;
+ double distance = (distanceData?.Distance ?? 0) * SpanCount;
// The velocity of the osu! hit object - calculated as the velocity of a slider
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
// The duration of the osu! hit object
double osuDuration = distance / osuVelocity;
EndTime = hitObject.StartTime + osuDuration;
- SegmentDuration = (EndTime - HitObject.StartTime) / spanCount;
+ SegmentDuration = (EndTime - HitObject.StartTime) / SpanCount;
}
public override IEnumerable Generate()
@@ -96,7 +95,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
return pattern;
}
- if (spanCount > 1)
+ if (SpanCount > 1)
{
if (SegmentDuration <= 90)
return generateRandomHoldNotes(HitObject.StartTime, 1);
@@ -104,7 +103,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (SegmentDuration <= 120)
{
convertType |= PatternType.ForceNotStack;
- return generateRandomNotes(HitObject.StartTime, spanCount + 1);
+ return generateRandomNotes(HitObject.StartTime, SpanCount + 1);
}
if (SegmentDuration <= 160)
@@ -117,7 +116,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (duration >= 4000)
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
- if (SegmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
+ if (SegmentDuration > 400 && SpanCount < TotalColumns - 1 - RandomStart)
return generateTiledHoldNotes(HitObject.StartTime);
return generateHoldAndNormalNotes(HitObject.StartTime);
@@ -251,7 +250,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
bool increasing = Random.NextDouble() > 0.5;
- for (int i = 0; i <= spanCount; i++)
+ for (int i = 0; i <= SpanCount; i++)
{
addToPattern(pattern, column, startTime, startTime);
startTime += SegmentDuration;
@@ -302,7 +301,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
- for (int i = 0; i <= spanCount; i++)
+ for (int i = 0; i <= SpanCount; i++)
{
addToPattern(pattern, nextColumn, startTime, startTime);
@@ -393,7 +392,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern();
- int columnRepeat = Math.Min(spanCount, TotalColumns);
+ int columnRepeat = Math.Min(SpanCount, TotalColumns);
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
@@ -447,7 +446,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var rowPattern = new Pattern();
- for (int i = 0; i <= spanCount; i++)
+ for (int i = 0; i <= SpanCount; i++)
{
if (!(ignoreHead && startTime == HitObject.StartTime))
{
diff --git a/osu.Game.Rulesets.Mania/Edit/HoldNoteCompositionTool.cs b/osu.Game.Rulesets.Mania/Edit/HoldNoteCompositionTool.cs
index 295bf417c4..a5f10ed436 100644
--- a/osu.Game.Rulesets.Mania/Edit/HoldNoteCompositionTool.cs
+++ b/osu.Game.Rulesets.Mania/Edit/HoldNoteCompositionTool.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
@@ -14,6 +16,8 @@ namespace osu.Game.Rulesets.Mania.Edit
{
}
+ public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders);
+
public override PlacementBlueprint CreatePlacementBlueprint() => new HoldNotePlacementBlueprint();
}
}
diff --git a/osu.Game.Rulesets.Mania/Edit/NoteCompositionTool.cs b/osu.Game.Rulesets.Mania/Edit/NoteCompositionTool.cs
index 50b5f9a8fe..9f54152596 100644
--- a/osu.Game.Rulesets.Mania/Edit/NoteCompositionTool.cs
+++ b/osu.Game.Rulesets.Mania/Edit/NoteCompositionTool.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
@@ -15,6 +17,8 @@ namespace osu.Game.Rulesets.Mania.Edit
{
}
+ public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles);
+
public override PlacementBlueprint CreatePlacementBlueprint() => new NotePlacementBlueprint();
}
}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 37b34d1721..71ac85dd1b 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -12,7 +12,6 @@ using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
-using osu.Framework.Testing;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays.Types;
@@ -35,7 +34,6 @@ using osu.Game.Screens.Ranking.Statistics;
namespace osu.Game.Rulesets.Mania
{
- [ExcludeFromDynamicCompile]
public class ManiaRuleset : Ruleset, ILegacyRuleset
{
///
diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json
new file mode 100644
index 0000000000..229760cd1c
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json
@@ -0,0 +1,14 @@
+{
+ "Mappings": [{
+ "RandomW": 3083084786,
+ "RandomX": 273326509,
+ "RandomY": 273553282,
+ "RandomZ": 2659838971,
+ "StartTime": 4836,
+ "Objects": [{
+ "StartTime": 4836,
+ "EndTime": 4836,
+ "Column": 0
+ }]
+ }]
+}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/zero-length-slider.osu b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/zero-length-slider.osu
new file mode 100644
index 0000000000..9b8ac1f9db
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/zero-length-slider.osu
@@ -0,0 +1,20 @@
+osu file format v14
+
+[General]
+StackLeniency: 0.7
+Mode: 0
+
+[Difficulty]
+HPDrainRate:1
+CircleSize:4
+OverallDifficulty:1
+ApproachRate:9
+SliderMultiplier:2.5
+SliderTickRate:0.5
+
+[TimingPoints]
+34,431.654676258993,4,1,0,50,1,0
+4782,-66.6666666666667,4,1,0,20,0,0
+
+[HitObjects]
+15,199,4836,22,0,L,1,46.8750017881394
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
index cd3daf18a9..7d32895083 100644
--- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs
@@ -12,6 +12,7 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
+ [Timeout(10000)]
public class OsuBeatmapConversionTest : BeatmapConversionTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs
index 491d82b89e..2d3cc3c103 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
@@ -23,19 +22,19 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
{
Name = @"Circle Count",
Content = circles.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles),
},
new BeatmapStatistic
{
Name = @"Slider Count",
Content = sliders.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders),
},
new BeatmapStatistic
{
Name = @"Spinner Count",
Content = spinners.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners),
}
};
}
diff --git a/osu.Game.Rulesets.Osu/Edit/HitCircleCompositionTool.cs b/osu.Game.Rulesets.Osu/Edit/HitCircleCompositionTool.cs
index 9c94fe0e3d..5f7c8b77b0 100644
--- a/osu.Game.Rulesets.Osu/Edit/HitCircleCompositionTool.cs
+++ b/osu.Game.Rulesets.Osu/Edit/HitCircleCompositionTool.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
@@ -15,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{
}
+ public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles);
+
public override PlacementBlueprint CreatePlacementBlueprint() => new HitCirclePlacementBlueprint();
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index 37019a7a05..f87bd53ec3 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -38,6 +39,13 @@ namespace osu.Game.Rulesets.Osu.Edit
new SpinnerCompositionTool()
};
+ private readonly BindableBool distanceSnapToggle = new BindableBool(true) { Description = "Distance Snap" };
+
+ protected override IEnumerable Toggles => new[]
+ {
+ distanceSnapToggle
+ };
+
[BackgroundDependencyLoader]
private void load()
{
@@ -45,6 +53,7 @@ namespace osu.Game.Rulesets.Osu.Edit
EditorBeatmap.SelectedHitObjects.CollectionChanged += (_, __) => updateDistanceSnapGrid();
EditorBeatmap.PlacementObject.ValueChanged += _ => updateDistanceSnapGrid();
+ distanceSnapToggle.ValueChanged += _ => updateDistanceSnapGrid();
}
protected override ComposeBlueprintContainer CreateBlueprintContainer(IEnumerable hitObjects)
@@ -87,6 +96,10 @@ namespace osu.Game.Rulesets.Osu.Edit
{
distanceSnapGridContainer.Clear();
distanceSnapGridCache.Invalidate();
+ distanceSnapGrid = null;
+
+ if (!distanceSnapToggle.Value)
+ return;
switch (BlueprintContainer.CurrentTool)
{
diff --git a/osu.Game.Rulesets.Osu/Edit/SliderCompositionTool.cs b/osu.Game.Rulesets.Osu/Edit/SliderCompositionTool.cs
index a377deb35f..596224e5c6 100644
--- a/osu.Game.Rulesets.Osu/Edit/SliderCompositionTool.cs
+++ b/osu.Game.Rulesets.Osu/Edit/SliderCompositionTool.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
@@ -15,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{
}
+ public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders);
+
public override PlacementBlueprint CreatePlacementBlueprint() => new SliderPlacementBlueprint();
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/SpinnerCompositionTool.cs b/osu.Game.Rulesets.Osu/Edit/SpinnerCompositionTool.cs
index 0de0af8f8c..c5e90da3bd 100644
--- a/osu.Game.Rulesets.Osu/Edit/SpinnerCompositionTool.cs
+++ b/osu.Game.Rulesets.Osu/Edit/SpinnerCompositionTool.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
@@ -15,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{
}
+ public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners);
+
public override PlacementBlueprint CreatePlacementBlueprint() => new SpinnerPlacementBlueprint();
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index f527eb2312..7f4a0dcbbb 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -30,14 +30,12 @@ using osu.Game.Scoring;
using osu.Game.Skinning;
using System;
using System.Linq;
-using osu.Framework.Testing;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Statistics;
using osu.Game.Screens.Ranking.Statistics;
namespace osu.Game.Rulesets.Osu
{
- [ExcludeFromDynamicCompile]
public class OsuRuleset : Ruleset, ILegacyRuleset
{
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableOsuRuleset(this, beatmap, mods);
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs
index 21df49d80b..aad8b189d9 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs
@@ -18,6 +18,10 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
private const float shadow_portion = 1 - (OsuLegacySkinTransformer.LEGACY_CIRCLE_RADIUS / OsuHitObject.OBJECT_RADIUS);
+ protected new float CalculatedBorderPortion
+ // Roughly matches osu!stable's slider border portions.
+ => base.CalculatedBorderPortion * 0.77f;
+
public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, base.AccentColour.A * 0.70f);
protected override Color4 ColourAt(float position)
diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs
index d0c57b20c0..5e550a5d03 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs
@@ -12,6 +12,7 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Taiko.Tests
{
[TestFixture]
+ [Timeout(10000)]
public class TaikoBeatmapConversionTest : BeatmapConversionTest
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs
index b595f43fbb..16a0726c8c 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects;
@@ -22,20 +21,20 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
new BeatmapStatistic
{
Name = @"Hit Count",
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles),
Content = hits.ToString(),
- Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Drumroll Count",
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders),
Content = drumrolls.ToString(),
- Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Swell Count",
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners),
Content = swells.ToString(),
- Icon = FontAwesome.Regular.Circle
}
};
}
diff --git a/osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs b/osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs
index bf77c76670..587a4efecb 100644
--- a/osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs
+++ b/osu.Game.Rulesets.Taiko/Edit/DrumRollCompositionTool.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Taiko.Edit.Blueprints;
@@ -15,6 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Edit
{
}
+ public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders);
+
public override PlacementBlueprint CreatePlacementBlueprint() => new DrumRollPlacementBlueprint();
}
}
diff --git a/osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs b/osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs
index e877cf6240..3e97b4e322 100644
--- a/osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs
+++ b/osu.Game.Rulesets.Taiko/Edit/HitCompositionTool.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Taiko.Edit.Blueprints;
@@ -15,6 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Edit
{
}
+ public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles);
+
public override PlacementBlueprint CreatePlacementBlueprint() => new HitPlacementBlueprint();
}
}
diff --git a/osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs b/osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs
index a6191fcedc..918afde1dd 100644
--- a/osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs
+++ b/osu.Game.Rulesets.Taiko/Edit/SwellCompositionTool.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Taiko.Edit.Blueprints;
@@ -15,6 +17,8 @@ namespace osu.Game.Rulesets.Taiko.Edit
{
}
+ public override Drawable CreateIcon() => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners);
+
public override PlacementBlueprint CreatePlacementBlueprint() => new SwellPlacementBlueprint();
}
}
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index dbc32f2c3e..9d485e3f20 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -22,7 +22,6 @@ using osu.Game.Rulesets.Taiko.Scoring;
using osu.Game.Scoring;
using System;
using System.Linq;
-using osu.Framework.Testing;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Taiko.Edit;
using osu.Game.Rulesets.Taiko.Objects;
@@ -32,7 +31,6 @@ using osu.Game.Skinning;
namespace osu.Game.Rulesets.Taiko
{
- [ExcludeFromDynamicCompile]
public class TaikoRuleset : Ruleset, ILegacyRuleset
{
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => new DrawableTaikoRuleset(this, beatmap, mods);
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs
index 4a11e1785b..8b22309033 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs
@@ -10,6 +10,7 @@ using System.Text;
using NUnit.Framework;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
+using osu.Framework.IO.Stores;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Formats;
@@ -19,6 +20,7 @@ using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
+using osu.Game.Skinning;
using osu.Game.Tests.Resources;
namespace osu.Game.Tests.Beatmaps.Formats
@@ -26,18 +28,33 @@ namespace osu.Game.Tests.Beatmaps.Formats
[TestFixture]
public class LegacyBeatmapEncoderTest
{
- private static IEnumerable allBeatmaps => TestResources.GetStore().GetAvailableResources().Where(res => res.EndsWith(".osu"));
+ private static readonly DllResourceStore beatmaps_resource_store = TestResources.GetStore();
+
+ private static IEnumerable allBeatmaps = beatmaps_resource_store.GetAvailableResources().Where(res => res.EndsWith(".osu"));
[TestCaseSource(nameof(allBeatmaps))]
public void TestEncodeDecodeStability(string name)
{
- var decoded = decodeFromLegacy(TestResources.GetStore().GetStream(name));
- var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded));
+ var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name);
+ var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name);
- sort(decoded);
- sort(decodedAfterEncode);
+ sort(decoded.beatmap);
+ sort(decodedAfterEncode.beatmap);
- Assert.That(decodedAfterEncode.Serialize(), Is.EqualTo(decoded.Serialize()));
+ Assert.That(decodedAfterEncode.beatmap.Serialize(), Is.EqualTo(decoded.beatmap.Serialize()));
+ Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration));
+ }
+
+ private bool areComboColoursEqual(IHasComboColours a, IHasComboColours b)
+ {
+ // equal to null, no need to SequenceEqual
+ if (a.ComboColours == null && b.ComboColours == null)
+ return true;
+
+ if (a.ComboColours == null || b.ComboColours == null)
+ return false;
+
+ return a.ComboColours.SequenceEqual(b.ComboColours);
}
private void sort(IBeatmap beatmap)
@@ -50,18 +67,31 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
- private IBeatmap decodeFromLegacy(Stream stream)
+ private (IBeatmap beatmap, TestLegacySkin beatmapSkin) decodeFromLegacy(Stream stream, string name)
{
using (var reader = new LineBufferedReader(stream))
- return convert(new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(reader));
+ {
+ var beatmap = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(reader);
+ var beatmapSkin = new TestLegacySkin(beatmaps_resource_store, name);
+ return (convert(beatmap), beatmapSkin);
+ }
}
- private Stream encodeToLegacy(IBeatmap beatmap)
+ private class TestLegacySkin : LegacySkin
{
+ public TestLegacySkin(IResourceStore storage, string fileName)
+ : base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, storage, null, fileName)
+ {
+ }
+ }
+
+ private Stream encodeToLegacy((IBeatmap beatmap, ISkin beatmapSkin) fullBeatmap)
+ {
+ var (beatmap, beatmapSkin) = fullBeatmap;
var stream = new MemoryStream();
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
- new LegacyBeatmapEncoder(beatmap).Encode(writer);
+ new LegacyBeatmapEncoder(beatmap, beatmapSkin).Encode(writer);
stream.Position = 0;
diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
index 0151678db3..dd3dba1274 100644
--- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
+++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
@@ -15,8 +15,10 @@ using osu.Framework.Extensions;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.IO;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Resources;
+using osu.Game.Users;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
@@ -32,7 +34,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportWhenClosed()
{
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWhenClosed)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -49,7 +51,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenDelete()
{
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenDelete)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -70,7 +72,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenImport()
{
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImport)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -96,7 +98,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportThenImportWithReZip()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithReZip)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -154,7 +156,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportThenImportWithChangedFile()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithChangedFile)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -205,7 +207,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportThenImportWithDifferentFilename()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithDifferentFilename)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -257,7 +259,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportCorruptThenImport()
{
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportCorruptThenImport)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -299,7 +301,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestRollbackOnFailure()
{
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestRollbackOnFailure)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -376,7 +378,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenDeleteThenImport()
{
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenDeleteThenImport)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -404,7 +406,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set)
{
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"{nameof(TestImportThenDeleteThenImportWithOnlineIDMismatch)}-{set}"))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost(set.ToString()))
{
try
{
@@ -438,7 +440,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportWithDuplicateBeatmapIDs()
{
// unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithDuplicateBeatmapIDs)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -524,7 +526,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportWhenFileOpen()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWhenFileOpen)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -546,7 +548,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportWithDuplicateHashes()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithDuplicateHashes)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -588,7 +590,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportNestedStructure()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportNestedStructure)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -633,7 +635,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportWithIgnoredDirectoryInArchive()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithIgnoredDirectoryInArchive)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -687,7 +689,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestUpdateBeatmapInfo()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapInfo)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -717,7 +719,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestUpdateBeatmapFile()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapFile)))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -756,6 +758,63 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
+ [Test]
+ public void TestCreateNewEmptyBeatmap()
+ {
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
+ {
+ try
+ {
+ var osu = loadOsu(host);
+ var manager = osu.Dependencies.Get();
+
+ var working = manager.CreateNew(new OsuRuleset().RulesetInfo, User.SYSTEM_USER);
+
+ manager.Save(working.BeatmapInfo, working.Beatmap);
+
+ var retrievedSet = manager.GetAllUsableBeatmapSets()[0];
+
+ // Check that the new file is referenced correctly by attempting a retrieval
+ Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(retrievedSet.Beatmaps[0]).Beatmap;
+ Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(0));
+ }
+ finally
+ {
+ host.Exit();
+ }
+ }
+ }
+
+ [Test]
+ public void TestCreateNewBeatmapWithObject()
+ {
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
+ {
+ try
+ {
+ var osu = loadOsu(host);
+ var manager = osu.Dependencies.Get();
+
+ var working = manager.CreateNew(new OsuRuleset().RulesetInfo, User.SYSTEM_USER);
+
+ ((Beatmap)working.Beatmap).HitObjects.Add(new HitCircle { StartTime = 5000 });
+
+ manager.Save(working.BeatmapInfo, working.Beatmap);
+
+ var retrievedSet = manager.GetAllUsableBeatmapSets()[0];
+
+ // Check that the new file is referenced correctly by attempting a retrieval
+ Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(retrievedSet.Beatmaps[0]).Beatmap;
+ Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1));
+ Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000));
+ }
+ finally
+ {
+ host.Exit();
+ }
+ }
+ }
+
public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
{
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
diff --git a/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs
index ff17f23d50..b491157627 100644
--- a/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs
+++ b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs
@@ -351,7 +351,7 @@ namespace osu.Game.Tests.Editing
using (var encoded = new MemoryStream())
{
using (var sw = new StreamWriter(encoded))
- new LegacyBeatmapEncoder(beatmap).Encode(sw);
+ new LegacyBeatmapEncoder(beatmap, null).Encode(sw);
return encoded.ToArray();
}
diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
index 57f0d7e957..a4d20714fa 100644
--- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
+++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Tests.Scores.IO
[Test]
public async Task TestBasicImport()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestBasicImport"))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -66,7 +66,7 @@ namespace osu.Game.Tests.Scores.IO
[Test]
public async Task TestImportMods()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportMods"))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -92,7 +92,7 @@ namespace osu.Game.Tests.Scores.IO
[Test]
public async Task TestImportStatistics()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportStatistics"))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -122,7 +122,7 @@ namespace osu.Game.Tests.Scores.IO
[Test]
public async Task TestImportWithDeletedBeatmapSet()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWithDeletedBeatmapSet"))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
@@ -159,7 +159,7 @@ namespace osu.Game.Tests.Scores.IO
[Test]
public async Task TestOnlineScoreIsAvailableLocally()
{
- using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestOnlineScoreIsAvailableLocally"))
+ using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorChangeStates.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorChangeStates.cs
index 293a6e6869..c8a32d966f 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneEditorChangeStates.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorChangeStates.cs
@@ -18,6 +18,8 @@ namespace osu.Game.Tests.Visual.Editing
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
+ protected new TestEditor Editor => (TestEditor)base.Editor;
+
public override void SetUpSteps()
{
base.SetUpSteps();
@@ -35,6 +37,7 @@ namespace osu.Game.Tests.Visual.Editing
addUndoSteps();
AddAssert("no change occurred", () => hitObjectCount == editorBeatmap.HitObjects.Count);
+ AddAssert("no unsaved changes", () => !Editor.HasUnsavedChanges);
}
[Test]
@@ -47,6 +50,7 @@ namespace osu.Game.Tests.Visual.Editing
addRedoSteps();
AddAssert("no change occurred", () => hitObjectCount == editorBeatmap.HitObjects.Count);
+ AddAssert("no unsaved changes", () => !Editor.HasUnsavedChanges);
}
[Test]
@@ -64,9 +68,11 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("add hitobject", () => editorBeatmap.Add(expectedObject = new HitCircle { StartTime = 1000 }));
AddAssert("hitobject added", () => addedObject == expectedObject);
+ AddAssert("unsaved changes", () => Editor.HasUnsavedChanges);
addUndoSteps();
AddAssert("hitobject removed", () => removedObject == expectedObject);
+ AddAssert("no unsaved changes", () => !Editor.HasUnsavedChanges);
}
[Test]
@@ -94,6 +100,17 @@ namespace osu.Game.Tests.Visual.Editing
addRedoSteps();
AddAssert("hitobject added", () => addedObject.StartTime == expectedObject.StartTime); // Can't compare via equality (new hitobject instance)
AddAssert("no hitobject removed", () => removedObject == null);
+ AddAssert("unsaved changes", () => Editor.HasUnsavedChanges);
+ }
+
+ [Test]
+ public void TestAddObjectThenSaveHasNoUnsavedChanges()
+ {
+ AddStep("add hitobject", () => editorBeatmap.Add(new HitCircle { StartTime = 1000 }));
+
+ AddAssert("unsaved changes", () => Editor.HasUnsavedChanges);
+ AddStep("save changes", () => Editor.Save());
+ AddAssert("no unsaved changes", () => !Editor.HasUnsavedChanges);
}
[Test]
@@ -120,6 +137,7 @@ namespace osu.Game.Tests.Visual.Editing
addUndoSteps();
AddAssert("hitobject added", () => addedObject.StartTime == expectedObject.StartTime); // Can't compare via equality (new hitobject instance)
AddAssert("no hitobject removed", () => removedObject == null);
+ AddAssert("unsaved changes", () => Editor.HasUnsavedChanges); // 2 steps performed, 1 undone
}
[Test]
@@ -148,19 +166,24 @@ namespace osu.Game.Tests.Visual.Editing
addRedoSteps();
AddAssert("hitobject removed", () => removedObject.StartTime == expectedObject.StartTime); // Can't compare via equality (new hitobject instance after undo)
AddAssert("no hitobject added", () => addedObject == null);
+ AddAssert("no changes", () => !Editor.HasUnsavedChanges); // end result is empty beatmap, matching original state
}
- private void addUndoSteps() => AddStep("undo", () => ((TestEditor)Editor).Undo());
+ private void addUndoSteps() => AddStep("undo", () => Editor.Undo());
- private void addRedoSteps() => AddStep("redo", () => ((TestEditor)Editor).Redo());
+ private void addRedoSteps() => AddStep("redo", () => Editor.Redo());
protected override Editor CreateEditor() => new TestEditor();
- private class TestEditor : Editor
+ protected class TestEditor : Editor
{
public new void Undo() => base.Undo();
public new void Redo() => base.Redo();
+
+ public new void Save() => base.Save();
+
+ public new bool HasUnsavedChanges => base.HasUnsavedChanges;
}
}
}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs
index e4d7e025a8..0b52ae2b95 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorComposeRadioButtons.cs
@@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
using osu.Game.Screens.Edit.Components.RadioButtons;
namespace osu.Game.Tests.Visual.Editing
@@ -22,7 +23,7 @@ namespace osu.Game.Tests.Visual.Editing
{
new RadioButton("Item 1", () => { }),
new RadioButton("Item 2", () => { }),
- new RadioButton("Item 3", () => { }),
+ new RadioButton("Item 3", () => { }, () => new SpriteIcon { Icon = FontAwesome.Regular.Angry }),
new RadioButton("Item 4", () => { }),
new RadioButton("Item 5", () => { })
}
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs
new file mode 100644
index 0000000000..62e12158ab
--- /dev/null
+++ b/osu.Game.Tests/Visual/Editing/TestSceneSetupScreen.cs
@@ -0,0 +1,32 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Osu.Beatmaps;
+using osu.Game.Screens.Edit;
+using osu.Game.Screens.Edit.Setup;
+
+namespace osu.Game.Tests.Visual.Editing
+{
+ [TestFixture]
+ public class TestSceneSetupScreen : EditorClockTestScene
+ {
+ [Cached(typeof(EditorBeatmap))]
+ [Cached(typeof(IBeatSnapProvider))]
+ private readonly EditorBeatmap editorBeatmap;
+
+ public TestSceneSetupScreen()
+ {
+ editorBeatmap = new EditorBeatmap(new OsuBeatmap());
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
+ Child = new SetupScreen();
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs
new file mode 100644
index 0000000000..3ee0f4e720
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneOverlayActivation.cs
@@ -0,0 +1,54 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Game.Overlays;
+using osu.Game.Rulesets;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+ public class TestSceneOverlayActivation : OsuPlayerTestScene
+ {
+ protected new OverlayTestPlayer Player => base.Player as OverlayTestPlayer;
+
+ [Test]
+ public void TestGameplayOverlayActivation()
+ {
+ AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
+ }
+
+ [Test]
+ public void TestGameplayOverlayActivationPaused()
+ {
+ AddUntilStep("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
+ AddStep("pause gameplay", () => Player.Pause());
+ AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
+ }
+
+ [Test]
+ public void TestGameplayOverlayActivationReplayLoaded()
+ {
+ AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
+ AddStep("load a replay", () => Player.DrawableRuleset.HasReplayLoaded.Value = true);
+ AddAssert("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
+ }
+
+ [Test]
+ public void TestGameplayOverlayActivationBreaks()
+ {
+ AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
+ AddStep("seek to break", () => Player.GameplayClockContainer.Seek(Beatmap.Value.Beatmap.Breaks.First().StartTime));
+ AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
+ AddStep("seek to break end", () => Player.GameplayClockContainer.Seek(Beatmap.Value.Beatmap.Breaks.First().EndTime));
+ AddUntilStep("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
+ }
+
+ protected override TestPlayer CreatePlayer(Ruleset ruleset) => new OverlayTestPlayer();
+
+ protected class OverlayTestPlayer : TestPlayer
+ {
+ public new OverlayActivation OverlayActivationMode => base.OverlayActivationMode.Value;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs
index cdfb3beb19..f8fab784cc 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplaySettingsOverlay.cs
@@ -48,7 +48,10 @@ namespace osu.Game.Tests.Visual.Gameplay
private class ExampleContainer : PlayerSettingsGroup
{
- protected override string Title => @"example";
+ public ExampleContainer()
+ : base("example")
+ {
+ }
}
}
}
diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
new file mode 100644
index 0000000000..4cad2b19d5
--- /dev/null
+++ b/osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
@@ -0,0 +1,83 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Testing;
+using osu.Game.Beatmaps;
+using osu.Game.Input.Bindings;
+using osu.Game.Overlays;
+using osu.Game.Tests.Resources;
+using osu.Game.Tests.Visual.Navigation;
+
+namespace osu.Game.Tests.Visual.Menus
+{
+ public class TestSceneMusicActionHandling : OsuGameTestScene
+ {
+ private GlobalActionContainer globalActionContainer => Game.ChildrenOfType().First();
+
+ [Test]
+ public void TestMusicPlayAction()
+ {
+ AddStep("ensure playing something", () => Game.MusicController.EnsurePlayingSomething());
+ AddStep("toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay));
+ AddAssert("music paused", () => !Game.MusicController.IsPlaying && Game.MusicController.IsUserPaused);
+ AddStep("toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay));
+ AddAssert("music resumed", () => Game.MusicController.IsPlaying && !Game.MusicController.IsUserPaused);
+ }
+
+ [Test]
+ public void TestMusicNavigationActions()
+ {
+ int importId = 0;
+ Queue<(WorkingBeatmap working, TrackChangeDirection changeDirection)> trackChangeQueue = null;
+
+ // ensure we have at least two beatmaps available to identify the direction the music controller navigated to.
+ AddRepeatStep("import beatmap", () => Game.BeatmapManager.Import(new BeatmapSetInfo
+ {
+ Beatmaps = new List
+ {
+ new BeatmapInfo
+ {
+ BaseDifficulty = new BeatmapDifficulty(),
+ }
+ },
+ Metadata = new BeatmapMetadata
+ {
+ Artist = $"a test map {importId++}",
+ Title = "title",
+ }
+ }).Wait(), 5);
+
+ AddStep("import beatmap with track", () =>
+ {
+ var setWithTrack = Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).Result;
+ Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(setWithTrack.Beatmaps.First());
+ });
+
+ AddStep("bind to track change", () =>
+ {
+ trackChangeQueue = new Queue<(WorkingBeatmap, TrackChangeDirection)>();
+ Game.MusicController.TrackChanged += (working, changeDirection) => trackChangeQueue.Enqueue((working, changeDirection));
+ });
+
+ AddStep("seek track to 6 second", () => Game.MusicController.SeekTo(6000));
+ AddUntilStep("wait for current time to update", () => Game.MusicController.CurrentTrack.CurrentTime > 5000);
+
+ AddStep("press previous", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPrev));
+ AddAssert("no track change", () => trackChangeQueue.Count == 0);
+ AddUntilStep("track restarted", () => Game.MusicController.CurrentTrack.CurrentTime < 5000);
+
+ AddStep("press previous", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPrev));
+ AddAssert("track changed to previous", () =>
+ trackChangeQueue.Count == 1 &&
+ trackChangeQueue.Dequeue().changeDirection == TrackChangeDirection.Prev);
+
+ AddStep("press next", () => globalActionContainer.TriggerPressed(GlobalAction.MusicNext));
+ AddAssert("track changed to next", () =>
+ trackChangeQueue.Count == 1 &&
+ trackChangeQueue.Dequeue().changeDirection == TrackChangeDirection.Next);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs
index 3d225aa0a9..faea32f90f 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs
@@ -162,6 +162,28 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("item 2 has rate 2", () => Precision.AlmostEquals(2, ((OsuModDoubleTime)Room.Playlist.Last().RequiredMods[0]).SpeedChange.Value));
}
+ ///
+ /// Tests that the global mod instances are not retained by the rooms, as global mod instances are retained and re-used by the mod select overlay.
+ ///
+ [Test]
+ public void TestGlobalModInstancesNotRetained()
+ {
+ OsuModDoubleTime mod = null;
+
+ AddStep("set dt mod and store", () =>
+ {
+ SelectedMods.Value = new[] { new OsuModDoubleTime() };
+
+ // Mod select overlay replaces our mod.
+ mod = (OsuModDoubleTime)SelectedMods.Value[0];
+ });
+
+ AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
+
+ AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2);
+ AddAssert("item has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)Room.Playlist.First().RequiredMods[0]).SpeedChange.Value));
+ }
+
private class TestMatchSongSelect : MatchSongSelect
{
public new MatchBeatmapDetailArea BeatmapDetails => (MatchBeatmapDetailArea)base.BeatmapDetails;
diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
index b2e18849c9..a899d072ac 100644
--- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
+++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs
@@ -12,6 +12,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Scoring;
+using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
diff --git a/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs
index e60adcee34..8f20bcdcc1 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneFullscreenOverlay.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneFullscreenOverlay : OsuTestScene
{
- private FullscreenOverlay overlay;
+ private FullscreenOverlay overlay;
protected override void LoadComplete()
{
@@ -38,10 +38,10 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("fire count 3", () => fireCount == 3);
}
- private class TestFullscreenOverlay : FullscreenOverlay
+ private class TestFullscreenOverlay : FullscreenOverlay
{
public TestFullscreenOverlay()
- : base(OverlayColourScheme.Pink)
+ : base(OverlayColourScheme.Pink, null)
{
Children = new Drawable[]
{
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
index 48b718c04d..67cd720260 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
@@ -5,9 +5,9 @@ using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
-using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays;
+using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Select.Leaderboards;
@@ -53,53 +53,46 @@ namespace osu.Game.Tests.Visual.SongSelect
private void showPersonalBestWithNullPosition()
{
- leaderboard.TopScore = new APILegacyUserTopScoreInfo
+ leaderboard.TopScore = new ScoreInfo
{
- Position = null,
- Score = new APILegacyScoreInfo
+ Rank = ScoreRank.XH,
+ Accuracy = 1,
+ MaxCombo = 244,
+ TotalScore = 1707827,
+ Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock() },
+ User = new User
{
- Rank = ScoreRank.XH,
- Accuracy = 1,
- MaxCombo = 244,
- TotalScore = 1707827,
- Mods = new[] { new OsuModHidden().Acronym, new OsuModHardRock().Acronym, },
- User = new User
+ Id = 6602580,
+ Username = @"waaiiru",
+ Country = new Country
{
- Id = 6602580,
- Username = @"waaiiru",
- Country = new Country
- {
- FullName = @"Spain",
- FlagName = @"ES",
- },
+ FullName = @"Spain",
+ FlagName = @"ES",
},
- }
+ },
};
}
private void showPersonalBest()
{
- leaderboard.TopScore = new APILegacyUserTopScoreInfo
+ leaderboard.TopScore = new ScoreInfo
{
Position = 999,
- Score = new APILegacyScoreInfo
+ Rank = ScoreRank.XH,
+ Accuracy = 1,
+ MaxCombo = 244,
+ TotalScore = 1707827,
+ Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
+ User = new User
{
- Rank = ScoreRank.XH,
- Accuracy = 1,
- MaxCombo = 244,
- TotalScore = 1707827,
- Mods = new[] { new OsuModHidden().Acronym, new OsuModHardRock().Acronym, },
- User = new User
+ Id = 6602580,
+ Username = @"waaiiru",
+ Country = new Country
{
- Id = 6602580,
- Username = @"waaiiru",
- Country = new Country
- {
- FullName = @"Spain",
- FlagName = @"ES",
- },
+ FullName = @"Spain",
+ FlagName = @"ES",
},
- }
+ },
};
}
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs
index 0598324110..b8b8792b9b 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneUserTopScoreContainer.cs
@@ -6,11 +6,11 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osuTK.Graphics;
-using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Online.Leaderboards;
using osu.Game.Overlays;
+using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Rulesets.Osu.Mods;
-using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.SongSelect
@@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.SongSelect
public TestSceneUserTopScoreContainer()
{
- UserTopScoreContainer topScoreContainer;
+ UserTopScoreContainer topScoreContainer;
Add(dialogOverlay = new DialogOverlay
{
@@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.SongSelect
RelativeSizeAxes = Axes.Both,
Colour = Color4.DarkGreen,
},
- topScoreContainer = new UserTopScoreContainer
+ topScoreContainer = new UserTopScoreContainer(s => new LeaderboardScore(s, s.Position, false))
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
@@ -52,69 +52,60 @@ namespace osu.Game.Tests.Visual.SongSelect
var scores = new[]
{
- new APILegacyUserTopScoreInfo
+ new ScoreInfo
{
Position = 999,
- Score = new APILegacyScoreInfo
+ Rank = ScoreRank.XH,
+ Accuracy = 1,
+ MaxCombo = 244,
+ TotalScore = 1707827,
+ Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
+ User = new User
{
- Rank = ScoreRank.XH,
- Accuracy = 1,
- MaxCombo = 244,
- TotalScore = 1707827,
- Mods = new[] { new OsuModHidden().Acronym, new OsuModHardRock().Acronym, },
- User = new User
+ Id = 6602580,
+ Username = @"waaiiru",
+ Country = new Country
{
- Id = 6602580,
- Username = @"waaiiru",
- Country = new Country
- {
- FullName = @"Spain",
- FlagName = @"ES",
- },
+ FullName = @"Spain",
+ FlagName = @"ES",
},
- }
+ },
},
- new APILegacyUserTopScoreInfo
+ new ScoreInfo
{
Position = 110000,
- Score = new APILegacyScoreInfo
+ Rank = ScoreRank.X,
+ Accuracy = 1,
+ MaxCombo = 244,
+ TotalScore = 1707827,
+ User = new User
{
- Rank = ScoreRank.X,
- Accuracy = 1,
- MaxCombo = 244,
- TotalScore = 1707827,
- User = new User
+ Id = 4608074,
+ Username = @"Skycries",
+ Country = new Country
{
- Id = 4608074,
- Username = @"Skycries",
- Country = new Country
- {
- FullName = @"Brazil",
- FlagName = @"BR",
- },
+ FullName = @"Brazil",
+ FlagName = @"BR",
},
- }
+ },
},
- new APILegacyUserTopScoreInfo
+ new ScoreInfo
{
Position = 22333,
- Score = new APILegacyScoreInfo
+ Rank = ScoreRank.S,
+ Accuracy = 1,
+ MaxCombo = 244,
+ TotalScore = 1707827,
+ User = new User
{
- Rank = ScoreRank.S,
- Accuracy = 1,
- MaxCombo = 244,
- TotalScore = 1707827,
- User = new User
+ Id = 1541390,
+ Username = @"Toukai",
+ Country = new Country
{
- Id = 1541390,
- Username = @"Toukai",
- Country = new Country
- {
- FullName = @"Canada",
- FlagName = @"CA",
- },
+ FullName = @"Canada",
+ FlagName = @"CA",
},
- }
+ },
}
};
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs
index c14a1ddbf2..475ab0c414 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNowPlayingOverlay.cs
@@ -1,18 +1,11 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Collections.Generic;
-using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
-using osu.Framework.Audio;
using osu.Framework.Graphics;
-using osu.Framework.Platform;
-using osu.Game.Beatmaps;
using osu.Game.Overlays;
-using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
-using osu.Game.Tests.Resources;
namespace osu.Game.Tests.Visual.UserInterface
{
@@ -24,14 +17,9 @@ namespace osu.Game.Tests.Visual.UserInterface
private NowPlayingOverlay nowPlayingOverlay;
- private RulesetStore rulesets;
-
[BackgroundDependencyLoader]
- private void load(AudioManager audio, GameHost host)
+ private void load()
{
- Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
- Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
-
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
nowPlayingOverlay = new NowPlayingOverlay
@@ -51,49 +39,5 @@ namespace osu.Game.Tests.Visual.UserInterface
AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state);
AddStep(@"hide", () => nowPlayingOverlay.Hide());
}
-
- private BeatmapManager manager { get; set; }
-
- private int importId;
-
- [Test]
- public void TestPrevTrackBehavior()
- {
- // ensure we have at least two beatmaps available.
- AddRepeatStep("import beatmap", () => manager.Import(new BeatmapSetInfo
- {
- Beatmaps = new List
- {
- new BeatmapInfo
- {
- BaseDifficulty = new BeatmapDifficulty(),
- }
- },
- Metadata = new BeatmapMetadata
- {
- Artist = $"a test map {importId++}",
- Title = "title",
- }
- }).Wait(), 5);
-
- WorkingBeatmap currentBeatmap = null;
-
- AddStep("import beatmap with track", () =>
- {
- var setWithTrack = manager.Import(TestResources.GetTestBeatmapForImport()).Result;
- Beatmap.Value = currentBeatmap = manager.GetWorkingBeatmap(setWithTrack.Beatmaps.First());
- });
-
- AddStep(@"Seek track to 6 second", () => musicController.SeekTo(6000));
- AddUntilStep(@"Wait for current time to update", () => musicController.CurrentTrack.CurrentTime > 5000);
-
- AddStep(@"Set previous", () => musicController.PreviousTrack());
-
- AddAssert(@"Check beatmap didn't change", () => currentBeatmap == Beatmap.Value);
- AddUntilStep("Wait for current time to update", () => musicController.CurrentTrack.CurrentTime < 5000);
-
- AddStep(@"Set previous", () => musicController.PreviousTrack());
- AddAssert(@"Check beatmap did change", () => currentBeatmap != Beatmap.Value);
- }
}
}
diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs
index fa530ea2c4..b60eb814e5 100644
--- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs
+++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs
@@ -20,8 +20,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
{
private const int padding = 10;
- protected override string Title => @"ladder";
-
private SettingsDropdown roundDropdown;
private PlayerCheckbox losersCheckbox;
private DateTextBox dateTimeBox;
@@ -34,6 +32,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
[Resolved]
private LadderInfo ladderInfo { get; set; }
+ public LadderEditorSettings()
+ : base("ladder")
+ {
+ }
+
[BackgroundDependencyLoader]
private void load()
{
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 3860f12baa..c5be5810e9 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Newtonsoft.Json;
+using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
@@ -14,6 +15,7 @@ using osu.Game.Scoring;
namespace osu.Game.Beatmaps
{
+ [ExcludeFromDynamicCompile]
[Serializable]
public class BeatmapInfo : IEquatable, IJsonSerializable, IHasPrimaryKey
{
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 0cadcf5947..e9f41f6bff 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -27,6 +27,8 @@ using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
+using osu.Game.Users;
+using osu.Game.Skinning;
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
namespace osu.Game.Beatmaps
@@ -64,12 +66,14 @@ namespace osu.Game.Beatmaps
private readonly RulesetStore rulesets;
private readonly BeatmapStore beatmaps;
private readonly AudioManager audioManager;
- private readonly BeatmapOnlineLookupQueue onlineLookupQueue;
private readonly TextureStore textureStore;
private readonly ITrackStore trackStore;
+ [CanBeNull]
+ private readonly BeatmapOnlineLookupQueue onlineLookupQueue;
+
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host = null,
- WorkingBeatmap defaultBeatmap = null)
+ WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false)
: base(storage, contextFactory, api, new BeatmapStore(contextFactory), host)
{
this.rulesets = rulesets;
@@ -83,7 +87,8 @@ namespace osu.Game.Beatmaps
beatmaps.ItemRemoved += removeWorkingCache;
beatmaps.ItemUpdated += removeWorkingCache;
- onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
+ if (performOnlineLookups)
+ onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
textureStore = new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store));
trackStore = audioManager.GetTrackStore(Files.Store);
@@ -94,6 +99,34 @@ namespace osu.Game.Beatmaps
protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz";
+ public WorkingBeatmap CreateNew(RulesetInfo ruleset, User user)
+ {
+ var metadata = new BeatmapMetadata
+ {
+ Artist = "artist",
+ Title = "title",
+ Author = user,
+ };
+
+ var set = new BeatmapSetInfo
+ {
+ Metadata = metadata,
+ Beatmaps = new List
+ {
+ new BeatmapInfo
+ {
+ BaseDifficulty = new BeatmapDifficulty(),
+ Ruleset = ruleset,
+ Metadata = metadata,
+ Version = "difficulty"
+ }
+ }
+ };
+
+ var working = Import(set).Result;
+ return GetWorkingBeatmap(working.Beatmaps.First());
+ }
+
protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
{
if (archive != null)
@@ -112,7 +145,8 @@ namespace osu.Game.Beatmaps
bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0);
- await onlineLookupQueue.UpdateAsync(beatmapSet, cancellationToken);
+ if (onlineLookupQueue != null)
+ await onlineLookupQueue.UpdateAsync(beatmapSet, cancellationToken);
// ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID.
if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0))
@@ -198,24 +232,35 @@ namespace osu.Game.Beatmaps
///
/// The to save the content against. The file referenced by will be replaced.
/// The content to write.
- public void Save(BeatmapInfo info, IBeatmap beatmapContent)
+ /// The beatmap content to write, null if to be omitted.
+ public void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null)
{
- var setInfo = QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == info.ID));
+ var setInfo = info.BeatmapSet;
using (var stream = new MemoryStream())
{
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
- new LegacyBeatmapEncoder(beatmapContent).Encode(sw);
+ new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw);
stream.Seek(0, SeekOrigin.Begin);
using (ContextFactory.GetForWrite())
{
var beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == info.ID);
+ var metadata = beatmapInfo.Metadata ?? setInfo.Metadata;
+
+ // grab the original file (or create a new one if not found).
+ var fileInfo = setInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, beatmapInfo.Path, StringComparison.OrdinalIgnoreCase)) ?? new BeatmapSetFileInfo();
+
+ // metadata may have changed; update the path with the standard format.
+ beatmapInfo.Path = $"{metadata.Artist} - {metadata.Title} ({metadata.Author}) [{beatmapInfo.Version}].osu";
beatmapInfo.MD5Hash = stream.ComputeMD5Hash();
+ // update existing or populate new file's filename.
+ fileInfo.Filename = beatmapInfo.Path;
+
stream.Seek(0, SeekOrigin.Begin);
- UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream);
+ UpdateFile(setInfo, fileInfo, stream);
}
}
diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
index 92199789ec..362c99ea3f 100644
--- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
@@ -33,6 +33,9 @@ namespace osu.Game.Beatmaps
protected override IBeatmap GetBeatmap()
{
+ if (BeatmapInfo.Path == null)
+ return new Beatmap { BeatmapInfo = BeatmapInfo };
+
try
{
using (var stream = new LineBufferedReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
@@ -67,6 +70,9 @@ namespace osu.Game.Beatmaps
protected override Track GetBeatmapTrack()
{
+ if (Metadata?.AudioFile == null)
+ return null;
+
try
{
return trackStore.Get(getPathForFile(Metadata.AudioFile));
@@ -80,6 +86,9 @@ namespace osu.Game.Beatmaps
protected override Waveform GetWaveform()
{
+ if (Metadata?.AudioFile == null)
+ return null;
+
try
{
var trackData = store.GetStream(getPathForFile(Metadata.AudioFile));
diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs
index 775d78f1fb..39b3c23ddd 100644
--- a/osu.Game/Beatmaps/BeatmapMetadata.cs
+++ b/osu.Game/Beatmaps/BeatmapMetadata.cs
@@ -6,11 +6,13 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Newtonsoft.Json;
+using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Users;
namespace osu.Game.Beatmaps
{
+ [ExcludeFromDynamicCompile]
[Serializable]
public class BeatmapMetadata : IEquatable, IHasPrimaryKey
{
diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs
index a8b83dca38..b76d780860 100644
--- a/osu.Game/Beatmaps/BeatmapSetInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs
@@ -5,10 +5,12 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
+using osu.Framework.Testing;
using osu.Game.Database;
namespace osu.Game.Beatmaps
{
+ [ExcludeFromDynamicCompile]
public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete, IEquatable
{
public int ID { get; set; }
diff --git a/osu.Game/Beatmaps/BeatmapStatistic.cs b/osu.Game/Beatmaps/BeatmapStatistic.cs
index 0745ec5222..9d87a20d60 100644
--- a/osu.Game/Beatmaps/BeatmapStatistic.cs
+++ b/osu.Game/Beatmaps/BeatmapStatistic.cs
@@ -1,14 +1,31 @@
// Copyright (c) ppy Pty Ltd . 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.Sprites;
+using osuTK;
namespace osu.Game.Beatmaps
{
public class BeatmapStatistic
{
- public IconUsage Icon;
+ [Obsolete("Use CreateIcon instead")] // can be removed 20210203
+ public IconUsage Icon = FontAwesome.Regular.QuestionCircle;
+
+ ///
+ /// A function to create the icon for display purposes. Use default icons available via whenever possible for conformity.
+ ///
+ public Func CreateIcon;
+
public string Content;
public string Name;
+
+ public BeatmapStatistic()
+ {
+#pragma warning disable 618
+ CreateIcon = () => new SpriteIcon { Icon = Icon, Scale = new Vector2(0.7f) };
+#pragma warning restore 618
+ }
}
}
diff --git a/osu.Game/Beatmaps/BeatmapStatisticIcon.cs b/osu.Game/Beatmaps/BeatmapStatisticIcon.cs
new file mode 100644
index 0000000000..181fb540df
--- /dev/null
+++ b/osu.Game/Beatmaps/BeatmapStatisticIcon.cs
@@ -0,0 +1,43 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using Humanizer;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+
+namespace osu.Game.Beatmaps
+{
+ ///
+ /// A default implementation of an icon used to represent beatmap statistics.
+ ///
+ public class BeatmapStatisticIcon : Sprite
+ {
+ private readonly BeatmapStatisticsIconType iconType;
+
+ public BeatmapStatisticIcon(BeatmapStatisticsIconType iconType)
+ {
+ this.iconType = iconType;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ Texture = textures.Get($"Icons/BeatmapDetails/{iconType.ToString().Kebaberize()}");
+ }
+ }
+
+ public enum BeatmapStatisticsIconType
+ {
+ Accuracy,
+ ApproachRate,
+ Bpm,
+ Circles,
+ HpDrain,
+ Length,
+ OverallDifficulty,
+ Size,
+ Sliders,
+ Spinners,
+ }
+}
diff --git a/osu.Game/Beatmaps/Formats/IHasCustomColours.cs b/osu.Game/Beatmaps/Formats/IHasCustomColours.cs
index 8f6c7dc328..dba3a37545 100644
--- a/osu.Game/Beatmaps/Formats/IHasCustomColours.cs
+++ b/osu.Game/Beatmaps/Formats/IHasCustomColours.cs
@@ -8,6 +8,6 @@ namespace osu.Game.Beatmaps.Formats
{
public interface IHasCustomColours
{
- Dictionary CustomColours { get; set; }
+ Dictionary CustomColours { get; }
}
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
index 57555cce90..80a4d6dea4 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs
@@ -7,13 +7,16 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
+using JetBrains.Annotations;
using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Skinning;
using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Beatmaps.Formats
{
@@ -23,9 +26,18 @@ namespace osu.Game.Beatmaps.Formats
private readonly IBeatmap beatmap;
- public LegacyBeatmapEncoder(IBeatmap beatmap)
+ [CanBeNull]
+ private readonly ISkin skin;
+
+ ///
+ /// Creates a new .
+ ///
+ /// The beatmap to encode.
+ /// The beatmap's skin, used for encoding combo colours.
+ public LegacyBeatmapEncoder(IBeatmap beatmap, [CanBeNull] ISkin skin)
{
this.beatmap = beatmap;
+ this.skin = skin;
if (beatmap.BeatmapInfo.RulesetID < 0 || beatmap.BeatmapInfo.RulesetID > 3)
throw new ArgumentException("Only beatmaps in the osu, taiko, catch, or mania rulesets can be encoded to the legacy beatmap format.", nameof(beatmap));
@@ -53,6 +65,9 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine();
handleControlPoints(writer);
+ writer.WriteLine();
+ handleColours(writer);
+
writer.WriteLine();
handleHitObjects(writer);
}
@@ -196,6 +211,28 @@ namespace osu.Game.Beatmaps.Formats
}
}
+ private void handleColours(TextWriter writer)
+ {
+ var colours = skin?.GetConfig>(GlobalSkinColours.ComboColours)?.Value;
+
+ if (colours == null || colours.Count == 0)
+ return;
+
+ writer.WriteLine("[Colours]");
+
+ for (var i = 0; i < colours.Count; i++)
+ {
+ var comboColour = colours[i];
+
+ writer.Write(FormattableString.Invariant($"Combo{i}: "));
+ writer.Write(FormattableString.Invariant($"{(byte)(comboColour.R * byte.MaxValue)},"));
+ writer.Write(FormattableString.Invariant($"{(byte)(comboColour.G * byte.MaxValue)},"));
+ writer.Write(FormattableString.Invariant($"{(byte)(comboColour.B * byte.MaxValue)},"));
+ writer.Write(FormattableString.Invariant($"{(byte)(comboColour.A * byte.MaxValue)}"));
+ writer.WriteLine();
+ }
+ }
+
private void handleHitObjects(TextWriter writer)
{
if (beatmap.HitObjects.Count == 0)
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 6a161e6e04..d9780233d1 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -13,6 +13,7 @@ using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
using osu.Framework.Statistics;
+using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Types;
@@ -22,6 +23,7 @@ using osu.Game.Storyboards;
namespace osu.Game.Beatmaps
{
+ [ExcludeFromDynamicCompile]
public abstract class WorkingBeatmap : IWorkingBeatmap
{
public readonly BeatmapInfo BeatmapInfo;
@@ -53,7 +55,7 @@ namespace osu.Game.Beatmaps
{
const double excess_length = 1000;
- var lastObject = Beatmap.HitObjects.LastOrDefault();
+ var lastObject = Beatmap?.HitObjects.LastOrDefault();
double length;
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index a8a8794320..71820dea55 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -6,6 +6,7 @@ using osu.Framework.Configuration;
using osu.Framework.Configuration.Tracking;
using osu.Framework.Extensions;
using osu.Framework.Platform;
+using osu.Framework.Testing;
using osu.Game.Overlays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select;
@@ -13,6 +14,7 @@ using osu.Game.Screens.Select.Filter;
namespace osu.Game.Configuration
{
+ [ExcludeFromDynamicCompile]
public class OsuConfigManager : IniConfigManager
{
protected override void InitialiseDefaults()
@@ -231,6 +233,6 @@ namespace osu.Game.Configuration
UIHoldActivationDelay,
HitLighting,
MenuBackgroundSource,
- GameplayDisableWinKey
+ GameplayDisableWinKey,
}
}
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 915d980d24..49d7edd56c 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -397,15 +397,24 @@ namespace osu.Game.Database
}
}
+ ///
+ /// Update an existing file, or create a new entry if not already part of the 's files.
+ ///
+ /// The item to operate on.
+ /// The file model to be updated or added.
+ /// The new file contents.
public void UpdateFile(TModel model, TFileModel file, Stream contents)
{
using (var usage = ContextFactory.GetForWrite())
{
// Dereference the existing file info, since the file model will be removed.
- Files.Dereference(file.FileInfo);
+ if (file.FileInfo != null)
+ {
+ Files.Dereference(file.FileInfo);
- // Remove the file model.
- usage.Context.Set().Remove(file);
+ // Remove the file model.
+ usage.Context.Set().Remove(file);
+ }
// Add the new file info and containing file model.
model.Files.Remove(file);
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index 751ccc8f15..41fd37a0d7 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -103,6 +103,8 @@ namespace osu.Game.Graphics.Containers
{
}
+ private bool playedPopInSound;
+
protected override void UpdateState(ValueChangedEvent state)
{
switch (state.NewValue)
@@ -110,16 +112,24 @@ namespace osu.Game.Graphics.Containers
case Visibility.Visible:
if (OverlayActivationMode.Value == OverlayActivation.Disabled)
{
+ // todo: visual/audible feedback that this operation could not complete.
State.Value = Visibility.Hidden;
return;
}
samplePopIn?.Play();
+ playedPopInSound = true;
+
if (BlockScreenWideMouse && DimMainContent) game?.AddBlockingOverlay(this);
break;
case Visibility.Hidden:
- samplePopOut?.Play();
+ if (playedPopInSound)
+ {
+ samplePopOut?.Play();
+ playedPopInSound = false;
+ }
+
if (BlockScreenWideMouse) game?.RemoveBlockingOverlay(this);
break;
}
diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs
index d739f56828..f32f8e0c67 100644
--- a/osu.Game/Graphics/Containers/SectionsContainer.cs
+++ b/osu.Game/Graphics/Containers/SectionsContainer.cs
@@ -26,8 +26,11 @@ namespace osu.Game.Graphics.Containers
{
if (value == expandableHeader) return;
- expandableHeader?.Expire();
+ if (expandableHeader != null)
+ RemoveInternal(expandableHeader);
+
expandableHeader = value;
+
if (value == null) return;
AddInternal(expandableHeader);
diff --git a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs
index 3015c44613..4c7f7957e9 100644
--- a/osu.Game/Graphics/Cursor/MenuCursorContainer.cs
+++ b/osu.Game/Graphics/Cursor/MenuCursorContainer.cs
@@ -5,6 +5,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
+using osu.Framework.Input.StateChanges;
namespace osu.Game.Graphics.Cursor
{
@@ -47,7 +48,10 @@ namespace osu.Game.Graphics.Cursor
{
base.Update();
- if (!CanShowCursor)
+ var lastMouseSource = inputManager.CurrentState.Mouse.LastSource;
+ bool hasValidInput = lastMouseSource != null && !(lastMouseSource is ISourcedFromTouch);
+
+ if (!hasValidInput || !CanShowCursor)
{
currentTarget?.Cursor?.Hide();
currentTarget = null;
diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
index fc3a7229fa..cc76c12975 100644
--- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
@@ -17,6 +17,8 @@ namespace osu.Game.Graphics.UserInterface
{
public class OsuDropdown : Dropdown, IHasAccentColour
{
+ private const float corner_radius = 4;
+
private Color4 accentColour;
public Color4 AccentColour
@@ -57,9 +59,11 @@ namespace osu.Game.Graphics.UserInterface
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
public OsuDropdownMenu()
{
- CornerRadius = 4;
+ CornerRadius = corner_radius;
BackgroundColour = Color4.Black.Opacity(0.5f);
+ MaskingContainer.CornerRadius = corner_radius;
+
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
ItemsContainer.Padding = new MarginPadding(5);
}
@@ -138,7 +142,7 @@ namespace osu.Game.Graphics.UserInterface
Foreground.Padding = new MarginPadding(2);
Masking = true;
- CornerRadius = 6;
+ CornerRadius = corner_radius;
}
[BackgroundDependencyLoader]
@@ -237,7 +241,7 @@ namespace osu.Game.Graphics.UserInterface
AutoSizeAxes = Axes.None;
Margin = new MarginPadding { Bottom = 4 };
- CornerRadius = 4;
+ CornerRadius = corner_radius;
Height = 40;
Foreground.Children = new Drawable[]
diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs
index ece1b8e22c..91a557094d 100644
--- a/osu.Game/Graphics/UserInterface/RollingCounter.cs
+++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs
@@ -9,16 +9,20 @@ using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Graphics.UserInterface
{
- public abstract class RollingCounter : Container
+ public abstract class RollingCounter : Container, IHasCurrentValue
where T : struct, IEquatable
{
- ///
- /// The current value.
- ///
- public Bindable Current = new Bindable();
+ private readonly BindableWithCurrent current = new BindableWithCurrent();
+
+ public Bindable Current
+ {
+ get => current.Current;
+ set => current.Current = value;
+ }
private SpriteText displayedCountSpriteText;
diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs
index 2cbe095d0b..290aba3468 100644
--- a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs
+++ b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs
@@ -3,6 +3,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
@@ -32,6 +33,11 @@ namespace osu.Game.Graphics.UserInterfaceV2
set => Component.Text = value;
}
+ public Container TabbableContentContainer
+ {
+ set => Component.TabbableContentContainer = value;
+ }
+
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs
index 1dd3afbfae..5b2549d2ee 100644
--- a/osu.Game/IO/WrappedStorage.cs
+++ b/osu.Game/IO/WrappedStorage.cs
@@ -25,7 +25,13 @@ namespace osu.Game.IO
this.subPath = subPath;
}
- protected virtual string MutatePath(string path) => !string.IsNullOrEmpty(subPath) ? Path.Combine(subPath, path) : path;
+ protected virtual string MutatePath(string path)
+ {
+ if (path == null)
+ return null;
+
+ return !string.IsNullOrEmpty(subPath) ? Path.Combine(subPath, path) : path;
+ }
protected virtual void ChangeTargetStorage(Storage newStorage)
{
diff --git a/osu.Game/Input/GameIdleTracker.cs b/osu.Game/Input/GameIdleTracker.cs
new file mode 100644
index 0000000000..260be7e5c9
--- /dev/null
+++ b/osu.Game/Input/GameIdleTracker.cs
@@ -0,0 +1,25 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Input;
+
+namespace osu.Game.Input
+{
+ public class GameIdleTracker : IdleTracker
+ {
+ private InputManager inputManager;
+
+ public GameIdleTracker(int time)
+ : base(time)
+ {
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ inputManager = GetContainingInputManager();
+ }
+
+ protected override bool AllowIdle => inputManager.FocusedDrawable == null;
+ }
+}
diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScores.cs b/osu.Game/Online/API/Requests/Responses/APILegacyScores.cs
index 75be9171b0..009639c1dc 100644
--- a/osu.Game/Online/API/Requests/Responses/APILegacyScores.cs
+++ b/osu.Game/Online/API/Requests/Responses/APILegacyScores.cs
@@ -3,6 +3,8 @@
using System.Collections.Generic;
using Newtonsoft.Json;
+using osu.Game.Rulesets;
+using osu.Game.Scoring;
namespace osu.Game.Online.API.Requests.Responses
{
@@ -22,5 +24,12 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"score")]
public APILegacyScoreInfo Score;
+
+ public ScoreInfo CreateScoreInfo(RulesetStore rulesets)
+ {
+ var score = Score.CreateScoreInfo(rulesets);
+ score.Position = Position;
+ return score;
+ }
}
}
diff --git a/osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs b/osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs
index 0bba6a93bd..bcc8721400 100644
--- a/osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIUserScoreAggregate.cs
@@ -33,6 +33,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty("user")]
public User User { get; set; }
+ [JsonProperty("position")]
+ public int? Position { get; set; }
+
public ScoreInfo CreateScoreInfo() =>
new ScoreInfo
{
@@ -40,6 +43,7 @@ namespace osu.Game.Online.API.Requests.Responses
PP = PP,
TotalScore = TotalScore,
User = User,
+ Position = Position
};
}
}
diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs
index 800029ceb9..084ba89f6e 100644
--- a/osu.Game/Online/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Online/Leaderboards/Leaderboard.cs
@@ -27,6 +27,7 @@ namespace osu.Game.Online.Leaderboards
private readonly OsuScrollContainer scrollContainer;
private readonly Container placeholderContainer;
+ private readonly UserTopScoreContainer topScoreContainer;
private FillFlowContainer scrollFlow;
@@ -55,13 +56,14 @@ namespace osu.Game.Online.Leaderboards
scrollFlow?.FadeOut(fade_duration, Easing.OutQuint).Expire();
scrollFlow = null;
- loading.Hide();
-
showScoresDelegate?.Cancel();
showScoresCancellationSource?.Cancel();
if (scores == null || !scores.Any())
+ {
+ loading.Hide();
return;
+ }
// ensure placeholder is hidden when displaying scores
PlaceholderState = PlaceholderState.Successful;
@@ -83,10 +85,25 @@ namespace osu.Game.Online.Leaderboards
}
scrollContainer.ScrollTo(0f, false);
+ loading.Hide();
}, (showScoresCancellationSource = new CancellationTokenSource()).Token));
}
}
+ public TScoreInfo TopScore
+ {
+ get => topScoreContainer.Score.Value;
+ set
+ {
+ topScoreContainer.Score.Value = value;
+
+ if (value == null)
+ topScoreContainer.Hide();
+ else
+ topScoreContainer.Show();
+ }
+ }
+
protected virtual FillFlowContainer CreateScoreFlow()
=> new FillFlowContainer
{
@@ -198,8 +215,9 @@ namespace osu.Game.Online.Leaderboards
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
+ Child = topScoreContainer = new UserTopScoreContainer(CreateDrawableTopScore)
},
- }
+ },
},
},
},
@@ -367,5 +385,7 @@ namespace osu.Game.Online.Leaderboards
}
protected abstract LeaderboardScore CreateDrawableScore(TScoreInfo model, int index);
+
+ protected abstract LeaderboardScore CreateDrawableTopScore(TScoreInfo model);
}
}
diff --git a/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs b/osu.Game/Online/Leaderboards/UserTopScoreContainer.cs
similarity index 77%
rename from osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs
rename to osu.Game/Online/Leaderboards/UserTopScoreContainer.cs
index 8e10734454..ab4210251e 100644
--- a/osu.Game/Screens/Select/Leaderboards/UserTopScoreContainer.cs
+++ b/osu.Game/Online/Leaderboards/UserTopScoreContainer.cs
@@ -9,31 +9,29 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
-using osu.Game.Online.API.Requests.Responses;
-using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets;
-using osu.Game.Scoring;
using osuTK;
-namespace osu.Game.Screens.Select.Leaderboards
+namespace osu.Game.Online.Leaderboards
{
- public class UserTopScoreContainer : VisibilityContainer
+ public class UserTopScoreContainer : VisibilityContainer
{
private const int duration = 500;
+ public Bindable Score = new Bindable();
+
private readonly Container scoreContainer;
-
- public Bindable Score = new Bindable();
-
- public Action ScoreSelected;
+ private readonly Func createScoreDelegate;
protected override bool StartHidden => true;
[Resolved]
private RulesetStore rulesets { get; set; }
- public UserTopScoreContainer()
+ public UserTopScoreContainer(Func createScoreDelegate)
{
+ this.createScoreDelegate = createScoreDelegate;
+
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
@@ -72,7 +70,7 @@ namespace osu.Game.Screens.Select.Leaderboards
private CancellationTokenSource loadScoreCancellation;
- private void onScoreChanged(ValueChangedEvent score)
+ private void onScoreChanged(ValueChangedEvent score)
{
var newScore = score.NewValue;
@@ -82,12 +80,7 @@ namespace osu.Game.Screens.Select.Leaderboards
if (newScore == null)
return;
- var scoreInfo = newScore.Score.CreateScoreInfo(rulesets);
-
- LoadComponentAsync(new LeaderboardScore(scoreInfo, newScore.Position, false)
- {
- Action = () => ScoreSelected?.Invoke(scoreInfo)
- }, drawableScore =>
+ LoadComponentAsync(createScoreDelegate(newScore), drawableScore =>
{
scoreContainer.Child = drawableScore;
drawableScore.FadeInFromZero(duration, Easing.OutQuint);
diff --git a/osu.Game/Online/Multiplayer/APILeaderboard.cs b/osu.Game/Online/Multiplayer/APILeaderboard.cs
new file mode 100644
index 0000000000..65863d6e0e
--- /dev/null
+++ b/osu.Game/Online/Multiplayer/APILeaderboard.cs
@@ -0,0 +1,18 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using Newtonsoft.Json;
+using osu.Game.Online.API.Requests.Responses;
+
+namespace osu.Game.Online.Multiplayer
+{
+ public class APILeaderboard
+ {
+ [JsonProperty("leaderboard")]
+ public List Leaderboard;
+
+ [JsonProperty("user_score")]
+ public APIUserScoreAggregate UserScore;
+ }
+}
diff --git a/osu.Game/Online/Multiplayer/GetRoomScoresRequest.cs b/osu.Game/Online/Multiplayer/GetRoomLeaderboardRequest.cs
similarity index 65%
rename from osu.Game/Online/Multiplayer/GetRoomScoresRequest.cs
rename to osu.Game/Online/Multiplayer/GetRoomLeaderboardRequest.cs
index bc913030dd..37c21457bc 100644
--- a/osu.Game/Online/Multiplayer/GetRoomScoresRequest.cs
+++ b/osu.Game/Online/Multiplayer/GetRoomLeaderboardRequest.cs
@@ -1,17 +1,15 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using System.Collections.Generic;
using osu.Game.Online.API;
-using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.Multiplayer
{
- public class GetRoomScoresRequest : APIRequest>
+ public class GetRoomLeaderboardRequest : APIRequest
{
private readonly int roomId;
- public GetRoomScoresRequest(int roomId)
+ public GetRoomLeaderboardRequest(int roomId)
{
this.roomId = roomId;
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 6b8e70e546..fec6640f2f 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -38,6 +38,7 @@ using osu.Game.Input;
using osu.Game.Overlays.Notifications;
using osu.Game.Input.Bindings;
using osu.Game.Online.Chat;
+using osu.Game.Overlays.Music;
using osu.Game.Skinning;
using osuTK.Graphics;
using osu.Game.Overlays.Volume;
@@ -647,6 +648,7 @@ namespace osu.Game
chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible;
Add(externalLinkOpener = new ExternalLinkOpener());
+ Add(new MusicKeyBindingHandler());
// side overlays which cancel each other.
var singleDisplaySideOverlays = new OverlayContainer[] { Settings, notifications };
@@ -698,9 +700,9 @@ namespace osu.Game
float offset = 0;
if (Settings.State.Value == Visibility.Visible)
- offset += ToolbarButton.WIDTH / 2;
+ offset += Toolbar.HEIGHT / 2;
if (notifications.State.Value == Visibility.Visible)
- offset -= ToolbarButton.WIDTH / 2;
+ offset -= Toolbar.HEIGHT / 2;
screenContainer.MoveToX(offset, SettingsPanel.TRANSITION_LENGTH, Easing.OutQuint);
}
@@ -718,24 +720,6 @@ namespace osu.Game
overlayContent.ChangeChildDepth(overlay, (float)-Clock.CurrentTime);
}
- public class GameIdleTracker : IdleTracker
- {
- private InputManager inputManager;
-
- public GameIdleTracker(int time)
- : base(time)
- {
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- inputManager = GetContainingInputManager();
- }
-
- protected override bool AllowIdle => inputManager.FocusedDrawable == null;
- }
-
private void forwardLoggedErrorsToNotifications()
{
int recentLogCount = 0;
@@ -991,10 +975,4 @@ namespace osu.Game
Exit();
}
}
-
- public enum ScorePresentType
- {
- Results,
- Gameplay
- }
}
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 51b9b7278d..b61017f038 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -198,7 +198,7 @@ namespace osu.Game
// ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup()
dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host));
- dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap));
+ dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap, true));
// this should likely be moved to ArchiveModelManager when another case appers where it is necessary
// to have inter-dependent model managers. this could be obtained with an IHasForeign interface to
@@ -250,10 +250,11 @@ namespace osu.Game
AddInternal(apiAccess);
AddInternal(RulesetConfigCache);
- GlobalActionContainer globalBinding;
-
MenuCursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both };
- MenuCursorContainer.Child = globalBinding = new GlobalActionContainer(this)
+
+ GlobalActionContainer globalBindings;
+
+ MenuCursorContainer.Child = globalBindings = new GlobalActionContainer(this)
{
RelativeSizeAxes = Axes.Both,
Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }
@@ -261,8 +262,8 @@ namespace osu.Game
base.Content.Add(CreateScalingContainer().WithChild(MenuCursorContainer));
- KeyBindingStore.Register(globalBinding);
- dependencies.Cache(globalBinding);
+ KeyBindingStore.Register(globalBindings);
+ dependencies.Cache(globalBindings);
PreviewTrackManager previewTrackManager;
dependencies.Cache(previewTrackManager = new PreviewTrackManager());
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs
index 1bab200fec..6a9a71210a 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs
@@ -12,7 +12,8 @@ namespace osu.Game.Overlays.BeatmapListing
public BeatmapListingTitle()
{
Title = "beatmap listing";
- IconTexture = "Icons/changelog";
+ Description = "browse for new beatmaps";
+ IconTexture = "Icons/Hexacons/beatmap";
}
}
}
diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs
index 225a8a0578..144af91145 100644
--- a/osu.Game/Overlays/BeatmapListingOverlay.cs
+++ b/osu.Game/Overlays/BeatmapListingOverlay.cs
@@ -24,7 +24,7 @@ using osuTK;
namespace osu.Game.Overlays
{
- public class BeatmapListingOverlay : FullscreenOverlay
+ public class BeatmapListingOverlay : FullscreenOverlay
{
[Resolved]
private PreviewTrackManager previewTrackManager { get; set; }
@@ -38,7 +38,7 @@ namespace osu.Game.Overlays
private OverlayScrollContainer resultScrollContainer;
public BeatmapListingOverlay()
- : base(OverlayColourScheme.Blue)
+ : base(OverlayColourScheme.Blue, new BeatmapListingHeader())
{
}
@@ -65,7 +65,7 @@ namespace osu.Game.Overlays
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- new BeatmapListingHeader(),
+ Header,
filterControl = new BeatmapListingFilterControl
{
SearchStarted = onSearchStarted,
diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs
index 4626589d81..6511b15fc8 100644
--- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs
+++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapSet
public BeatmapHeaderTitle()
{
Title = "beatmap info";
- IconTexture = "Icons/changelog";
+ IconTexture = "Icons/Hexacons/beatmap";
}
}
}
diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs
index 3e23442023..bbec62a85a 100644
--- a/osu.Game/Overlays/BeatmapSetOverlay.cs
+++ b/osu.Game/Overlays/BeatmapSetOverlay.cs
@@ -19,12 +19,14 @@ using osuTK;
namespace osu.Game.Overlays
{
- public class BeatmapSetOverlay : FullscreenOverlay
+ public class BeatmapSetOverlay : FullscreenOverlay // we don't provide a standard header for now.
{
public const float X_PADDING = 40;
public const float Y_PADDING = 25;
public const float RIGHT_WIDTH = 275;
- protected readonly Header Header;
+
+ //todo: should be an OverlayHeader? or maybe not?
+ protected new readonly Header Header;
[Resolved]
private RulesetStore rulesets { get; set; }
@@ -37,7 +39,7 @@ namespace osu.Game.Overlays
private readonly Box background;
public BeatmapSetOverlay()
- : base(OverlayColourScheme.Blue)
+ : base(OverlayColourScheme.Blue, null)
{
OverlayScrollContainer scroll;
Info info;
diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
index 050bdea03a..f4be4328e7 100644
--- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs
+++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
@@ -115,7 +115,8 @@ namespace osu.Game.Overlays.Changelog
public ChangelogHeaderTitle()
{
Title = "changelog";
- IconTexture = "Icons/changelog";
+ Description = "track recent dev updates in the osu! ecosystem";
+ IconTexture = "Icons/Hexacons/devtools";
}
}
}
diff --git a/osu.Game/Overlays/ChangelogOverlay.cs b/osu.Game/Overlays/ChangelogOverlay.cs
index 726be9e194..c7e9a86fa4 100644
--- a/osu.Game/Overlays/ChangelogOverlay.cs
+++ b/osu.Game/Overlays/ChangelogOverlay.cs
@@ -21,12 +21,10 @@ using osu.Game.Overlays.Changelog;
namespace osu.Game.Overlays
{
- public class ChangelogOverlay : FullscreenOverlay
+ public class ChangelogOverlay : FullscreenOverlay
{
public readonly Bindable Current = new Bindable();
- protected ChangelogHeader Header;
-
private Container content;
private SampleChannel sampleBack;
@@ -36,7 +34,7 @@ namespace osu.Game.Overlays
protected List Streams;
public ChangelogOverlay()
- : base(OverlayColourScheme.Purple)
+ : base(OverlayColourScheme.Purple, new ChangelogHeader())
{
}
@@ -61,10 +59,11 @@ namespace osu.Game.Overlays
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- Header = new ChangelogHeader
+ Header.With(h =>
{
- ListingSelected = ShowListing,
- },
+ h.ListingSelected = ShowListing;
+ h.Build.BindTarget = Current;
+ }),
content = new Container
{
RelativeSizeAxes = Axes.X,
@@ -77,8 +76,6 @@ namespace osu.Game.Overlays
sampleBack = audio.Samples.Get(@"UI/generic-select-soft");
- Header.Build.BindTo(Current);
-
Current.BindValueChanged(e =>
{
if (e.NewValue != null)
diff --git a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs
index b46ca6b040..be9ecc6746 100644
--- a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs
+++ b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Chat.Selection
{
public class ChannelSelectionOverlay : WaveOverlayContainer
{
- public const float WIDTH_PADDING = 170;
+ public new const float WIDTH_PADDING = 170;
private const float transition_duration = 500;
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index 692175603c..25a59e9b25 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -26,8 +26,12 @@ using osu.Framework.Graphics.Sprites;
namespace osu.Game.Overlays
{
- public class ChatOverlay : OsuFocusedOverlayContainer
+ public class ChatOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent
{
+ public string IconTexture => "Icons/Hexacons/messaging";
+ public string Title => "chat";
+ public string Description => "join the real-time discussion";
+
private const float textbox_height = 60;
private const float channel_selection_min_height = 0.3f;
diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs
index 9ee679a866..36bf589877 100644
--- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs
+++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs
@@ -12,7 +12,8 @@ namespace osu.Game.Overlays.Dashboard
public DashboardTitle()
{
Title = "dashboard";
- IconTexture = "Icons/changelog";
+ Description = "view your friends and other information";
+ IconTexture = "Icons/Hexacons/social";
}
}
}
diff --git a/osu.Game/Overlays/DashboardOverlay.cs b/osu.Game/Overlays/DashboardOverlay.cs
index e3a4b0e152..8135b83a03 100644
--- a/osu.Game/Overlays/DashboardOverlay.cs
+++ b/osu.Game/Overlays/DashboardOverlay.cs
@@ -15,17 +15,21 @@ using osu.Game.Overlays.Dashboard.Friends;
namespace osu.Game.Overlays
{
- public class DashboardOverlay : FullscreenOverlay
+ public class DashboardOverlay : FullscreenOverlay
{
private CancellationTokenSource cancellationToken;
private Container content;
- private DashboardOverlayHeader header;
private LoadingLayer loading;
private OverlayScrollContainer scrollFlow;
public DashboardOverlay()
- : base(OverlayColourScheme.Purple)
+ : base(OverlayColourScheme.Purple, new DashboardOverlayHeader
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Depth = -float.MaxValue
+ })
{
}
@@ -50,12 +54,7 @@ namespace osu.Game.Overlays
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- header = new DashboardOverlayHeader
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Depth = -float.MaxValue
- },
+ Header,
content = new Container
{
RelativeSizeAxes = Axes.X,
@@ -72,7 +71,7 @@ namespace osu.Game.Overlays
{
base.LoadComplete();
- header.Current.BindValueChanged(onTabChanged);
+ Header.Current.BindValueChanged(onTabChanged);
}
private bool displayUpdateRequired = true;
@@ -84,7 +83,7 @@ namespace osu.Game.Overlays
// We don't want to create a new display on every call, only when exiting from fully closed state.
if (displayUpdateRequired)
{
- header.Current.TriggerChange();
+ Header.Current.TriggerChange();
displayUpdateRequired = false;
}
}
@@ -136,7 +135,7 @@ namespace osu.Game.Overlays
if (State.Value == Visibility.Hidden)
return;
- header.Current.TriggerChange();
+ Header.Current.TriggerChange();
}
protected override void Dispose(bool isDisposing)
diff --git a/osu.Game/Overlays/FullscreenOverlay.cs b/osu.Game/Overlays/FullscreenOverlay.cs
index 3464ce6086..bd6b07c65f 100644
--- a/osu.Game/Overlays/FullscreenOverlay.cs
+++ b/osu.Game/Overlays/FullscreenOverlay.cs
@@ -12,16 +12,25 @@ using osuTK.Graphics;
namespace osu.Game.Overlays
{
- public abstract class FullscreenOverlay : WaveOverlayContainer, IOnlineComponent
+ public abstract class FullscreenOverlay : WaveOverlayContainer, IOnlineComponent, INamedOverlayComponent
+ where T : OverlayHeader
{
+ public virtual string IconTexture => Header?.Title.IconTexture ?? string.Empty;
+ public virtual string Title => Header?.Title.Title ?? string.Empty;
+ public virtual string Description => Header?.Title.Description ?? string.Empty;
+
+ public T Header { get; }
+
[Resolved]
protected IAPIProvider API { get; private set; }
[Cached]
protected readonly OverlayColourProvider ColourProvider;
- protected FullscreenOverlay(OverlayColourScheme colourScheme)
+ protected FullscreenOverlay(OverlayColourScheme colourScheme, T header)
{
+ Header = header;
+
ColourProvider = new OverlayColourProvider(colourScheme);
RelativeSizeAxes = Axes.Both;
diff --git a/osu.Game/Overlays/INamedOverlayComponent.cs b/osu.Game/Overlays/INamedOverlayComponent.cs
new file mode 100644
index 0000000000..38fb8679a0
--- /dev/null
+++ b/osu.Game/Overlays/INamedOverlayComponent.cs
@@ -0,0 +1,14 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Game.Overlays
+{
+ public interface INamedOverlayComponent
+ {
+ string IconTexture { get; }
+
+ string Title { get; }
+
+ string Description { get; }
+ }
+}
diff --git a/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs
new file mode 100644
index 0000000000..e6edfb1e3e
--- /dev/null
+++ b/osu.Game/Overlays/Music/MusicKeyBindingHandler.cs
@@ -0,0 +1,81 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Input.Bindings;
+using osu.Game.Beatmaps;
+using osu.Game.Input.Bindings;
+using osu.Game.Overlays.OSD;
+
+namespace osu.Game.Overlays.Music
+{
+ ///
+ /// Handles s related to music playback, and displays s via the global accordingly.
+ ///
+ public class MusicKeyBindingHandler : Component, IKeyBindingHandler
+ {
+ [Resolved]
+ private IBindable beatmap { get; set; }
+
+ [Resolved]
+ private MusicController musicController { get; set; }
+
+ [Resolved(canBeNull: true)]
+ private OnScreenDisplay onScreenDisplay { get; set; }
+
+ public bool OnPressed(GlobalAction action)
+ {
+ if (beatmap.Disabled)
+ return false;
+
+ switch (action)
+ {
+ case GlobalAction.MusicPlay:
+ // use previous state as TogglePause may not update the track's state immediately (state update is run on the audio thread see https://github.com/ppy/osu/issues/9880#issuecomment-674668842)
+ bool wasPlaying = musicController.IsPlaying;
+
+ if (musicController.TogglePause())
+ onScreenDisplay?.Display(new MusicActionToast(wasPlaying ? "Pause track" : "Play track"));
+ return true;
+
+ case GlobalAction.MusicNext:
+ musicController.NextTrack(() => onScreenDisplay?.Display(new MusicActionToast("Next track")));
+
+ return true;
+
+ case GlobalAction.MusicPrev:
+ musicController.PreviousTrack(res =>
+ {
+ switch (res)
+ {
+ case PreviousTrackResult.Restart:
+ onScreenDisplay?.Display(new MusicActionToast("Restart track"));
+ break;
+
+ case PreviousTrackResult.Previous:
+ onScreenDisplay?.Display(new MusicActionToast("Previous track"));
+ break;
+ }
+ });
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public void OnReleased(GlobalAction action)
+ {
+ }
+
+ private class MusicActionToast : Toast
+ {
+ public MusicActionToast(string action)
+ : base("Music Playback", action, string.Empty)
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index 17877a69a5..b568e4d02b 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -12,12 +12,9 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input.Bindings;
using osu.Framework.Utils;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
-using osu.Game.Input.Bindings;
-using osu.Game.Overlays.OSD;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Overlays
@@ -25,7 +22,7 @@ namespace osu.Game.Overlays
///
/// Handles playback of the global music track.
///
- public class MusicController : CompositeDrawable, IKeyBindingHandler
+ public class MusicController : CompositeDrawable
{
[Resolved]
private BeatmapManager beatmaps { get; set; }
@@ -62,9 +59,6 @@ namespace osu.Game.Overlays
[Resolved]
private IBindable> mods { get; set; }
- [Resolved(canBeNull: true)]
- private OnScreenDisplay onScreenDisplay { get; set; }
-
[NotNull]
public DrawableTrack CurrentTrack { get; private set; } = new DrawableTrack(new TrackVirtual(1000));
@@ -207,7 +201,13 @@ namespace osu.Game.Overlays
///
/// Play the previous track or restart the current track if it's current time below .
///
- public void PreviousTrack() => Schedule(() => prev());
+ /// Invoked when the operation has been performed successfully.
+ public void PreviousTrack(Action onSuccess = null) => Schedule(() =>
+ {
+ PreviousTrackResult res = prev();
+ if (res != PreviousTrackResult.None)
+ onSuccess?.Invoke(res);
+ });
///
/// Play the previous track or restart the current track if it's current time below .
@@ -243,7 +243,14 @@ namespace osu.Game.Overlays
///
/// Play the next random or playlist track.
///
- public void NextTrack() => Schedule(() => next());
+ /// Invoked when the operation has been performed successfully.
+ /// A of the operation.
+ public void NextTrack(Action onSuccess = null) => Schedule(() =>
+ {
+ bool res = next();
+ if (res)
+ onSuccess?.Invoke();
+ });
private bool next()
{
@@ -279,6 +286,11 @@ namespace osu.Game.Overlays
private void changeBeatmap(WorkingBeatmap newWorking)
{
+ // This method can potentially be triggered multiple times as it is eagerly fired in next() / prev() to ensure correct execution order
+ // (changeBeatmap must be called before consumers receive the bindable changed event, which is not the case when the local beatmap bindable is updated directly).
+ if (newWorking == current)
+ return;
+
var lastWorking = current;
TrackChangeDirection direction = TrackChangeDirection.None;
@@ -402,54 +414,6 @@ namespace osu.Game.Overlays
mod.ApplyToTrack(CurrentTrack);
}
}
-
- public bool OnPressed(GlobalAction action)
- {
- if (beatmap.Disabled)
- return false;
-
- switch (action)
- {
- case GlobalAction.MusicPlay:
- if (TogglePause())
- onScreenDisplay?.Display(new MusicControllerToast(IsPlaying ? "Play track" : "Pause track"));
- return true;
-
- case GlobalAction.MusicNext:
- if (next())
- onScreenDisplay?.Display(new MusicControllerToast("Next track"));
-
- return true;
-
- case GlobalAction.MusicPrev:
- switch (prev())
- {
- case PreviousTrackResult.Restart:
- onScreenDisplay?.Display(new MusicControllerToast("Restart track"));
- break;
-
- case PreviousTrackResult.Previous:
- onScreenDisplay?.Display(new MusicControllerToast("Previous track"));
- break;
- }
-
- return true;
- }
-
- return false;
- }
-
- public void OnReleased(GlobalAction action)
- {
- }
-
- public class MusicControllerToast : Toast
- {
- public MusicControllerToast(string action)
- : base("Music Playback", action, string.Empty)
- {
- }
- }
}
public enum TrackChangeDirection
diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs
index ddada2bdaf..63174128e7 100644
--- a/osu.Game/Overlays/News/NewsHeader.cs
+++ b/osu.Game/Overlays/News/NewsHeader.cs
@@ -57,7 +57,8 @@ namespace osu.Game.Overlays.News
public NewsHeaderTitle()
{
Title = "news";
- IconTexture = "Icons/news";
+ Description = "get up-to-date on community happenings";
+ IconTexture = "Icons/Hexacons/news";
}
}
}
diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs
index 09fb445b1f..c8c1db012f 100644
--- a/osu.Game/Overlays/NewsOverlay.cs
+++ b/osu.Game/Overlays/NewsOverlay.cs
@@ -13,17 +13,16 @@ using osu.Game.Overlays.News.Displays;
namespace osu.Game.Overlays
{
- public class NewsOverlay : FullscreenOverlay
+ public class NewsOverlay : FullscreenOverlay
{
private readonly Bindable article = new Bindable(null);
private Container content;
private LoadingLayer loading;
- private NewsHeader header;
private OverlayScrollContainer scrollFlow;
public NewsOverlay()
- : base(OverlayColourScheme.Purple)
+ : base(OverlayColourScheme.Purple, new NewsHeader())
{
}
@@ -48,10 +47,10 @@ namespace osu.Game.Overlays
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- header = new NewsHeader
+ Header.With(h =>
{
- ShowFrontPage = ShowFrontPage
- },
+ h.ShowFrontPage = ShowFrontPage;
+ }),
content = new Container
{
RelativeSizeAxes = Axes.X,
@@ -112,12 +111,12 @@ namespace osu.Game.Overlays
if (e.NewValue == null)
{
- header.SetFrontPage();
+ Header.SetFrontPage();
LoadDisplay(new FrontPageDisplay());
return;
}
- header.SetArticle(e.NewValue);
+ Header.SetArticle(e.NewValue);
LoadDisplay(Empty());
}
diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs
index 41160d10ec..b5714fbcae 100644
--- a/osu.Game/Overlays/NotificationOverlay.cs
+++ b/osu.Game/Overlays/NotificationOverlay.cs
@@ -16,8 +16,12 @@ using osu.Framework.Threading;
namespace osu.Game.Overlays
{
- public class NotificationOverlay : OsuFocusedOverlayContainer
+ public class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent
{
+ public string IconTexture => "Icons/Hexacons/notification";
+ public string Title => "notifications";
+ public string Description => "waiting for 'ya";
+
private const float width = 320;
public const float TRANSITION_LENGTH = 600;
diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs
index af692486b7..55adf02a45 100644
--- a/osu.Game/Overlays/NowPlayingOverlay.cs
+++ b/osu.Game/Overlays/NowPlayingOverlay.cs
@@ -25,8 +25,12 @@ using osuTK.Graphics;
namespace osu.Game.Overlays
{
- public class NowPlayingOverlay : OsuFocusedOverlayContainer
+ public class NowPlayingOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent
{
+ public string IconTexture => "Icons/Hexacons/music";
+ public string Title => "now playing";
+ public string Description => "manage the currently playing track";
+
private const float player_height = 130;
private const float transition_length = 800;
private const float progress_height = 10;
diff --git a/osu.Game/Overlays/OverlayHeader.cs b/osu.Game/Overlays/OverlayHeader.cs
index cc7f798c4a..fed1e57686 100644
--- a/osu.Game/Overlays/OverlayHeader.cs
+++ b/osu.Game/Overlays/OverlayHeader.cs
@@ -12,6 +12,8 @@ namespace osu.Game.Overlays
{
public abstract class OverlayHeader : Container
{
+ public OverlayTitle Title { get; }
+
private float contentSidePadding;
///
@@ -73,7 +75,7 @@ namespace osu.Game.Overlays
AutoSizeAxes = Axes.Y,
Children = new[]
{
- CreateTitle().With(title =>
+ Title = CreateTitle().With(title =>
{
title.Anchor = Anchor.CentreLeft;
title.Origin = Anchor.CentreLeft;
diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs
index e7415e6f74..b67d5db1a4 100644
--- a/osu.Game/Overlays/OverlayScrollContainer.cs
+++ b/osu.Game/Overlays/OverlayScrollContainer.cs
@@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Overlays
{
///
- /// which provides . Mostly used in .
+ /// which provides . Mostly used in .
///
public class OverlayScrollContainer : OsuScrollContainer
{
diff --git a/osu.Game/Overlays/OverlayTitle.cs b/osu.Game/Overlays/OverlayTitle.cs
index 1c9567428c..17eeece1f8 100644
--- a/osu.Game/Overlays/OverlayTitle.cs
+++ b/osu.Game/Overlays/OverlayTitle.cs
@@ -12,19 +12,27 @@ using osuTK;
namespace osu.Game.Overlays
{
- public abstract class OverlayTitle : CompositeDrawable
+ public abstract class OverlayTitle : CompositeDrawable, INamedOverlayComponent
{
- private readonly OsuSpriteText title;
+ private readonly OsuSpriteText titleText;
private readonly Container icon;
- protected string Title
+ private string title;
+
+ public string Title
{
- set => title.Text = value;
+ get => title;
+ protected set => titleText.Text = title = value;
}
- protected string IconTexture
+ public string Description { get; protected set; }
+
+ private string iconTexture;
+
+ public string IconTexture
{
- set => icon.Child = new OverlayTitleIcon(value);
+ get => iconTexture;
+ protected set => icon.Child = new OverlayTitleIcon(iconTexture = value);
}
protected OverlayTitle()
@@ -45,7 +53,7 @@ namespace osu.Game.Overlays
Margin = new MarginPadding { Horizontal = 5 }, // compensates for osu-web sprites having around 5px of whitespace on each side
Size = new Vector2(30)
},
- title = new OsuSpriteText
+ titleText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game/Overlays/OverlayView.cs b/osu.Game/Overlays/OverlayView.cs
index 3e2c54c726..312271316a 100644
--- a/osu.Game/Overlays/OverlayView.cs
+++ b/osu.Game/Overlays/OverlayView.cs
@@ -9,7 +9,7 @@ using osu.Game.Online.API;
namespace osu.Game.Overlays
{
///
- /// A subview containing online content, to be displayed inside a .
+ /// A subview containing online content, to be displayed inside a .
///
///
/// Automatically performs a data fetch on load.
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index 55474c9d3e..c947ef0781 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -97,7 +97,7 @@ namespace osu.Game.Overlays.Profile
public ProfileHeaderTitle()
{
Title = "player info";
- IconTexture = "Icons/profile";
+ IconTexture = "Icons/Hexacons/profile";
}
}
diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs
index e30c6f07a8..92e22f5873 100644
--- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs
+++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs
@@ -30,7 +30,8 @@ namespace osu.Game.Overlays.Rankings
public RankingsTitle()
{
Title = "ranking";
- IconTexture = "Icons/rankings";
+ Description = "find out who's the best right now";
+ IconTexture = "Icons/Hexacons/rankings";
}
}
}
diff --git a/osu.Game/Overlays/RankingsOverlay.cs b/osu.Game/Overlays/RankingsOverlay.cs
index 7b200d4226..ae6d49960a 100644
--- a/osu.Game/Overlays/RankingsOverlay.cs
+++ b/osu.Game/Overlays/RankingsOverlay.cs
@@ -17,17 +17,16 @@ using osu.Game.Overlays.Rankings.Tables;
namespace osu.Game.Overlays
{
- public class RankingsOverlay : FullscreenOverlay
+ public class RankingsOverlay : FullscreenOverlay
{
- protected Bindable Country => header.Country;
+ protected Bindable Country => Header.Country;
- protected Bindable Scope => header.Current;
+ protected Bindable Scope => Header.Current;
private readonly OverlayScrollContainer scrollFlow;
private readonly Container contentContainer;
private readonly LoadingLayer loading;
private readonly Box background;
- private readonly RankingsOverlayHeader header;
private APIRequest lastRequest;
private CancellationTokenSource cancellationToken;
@@ -36,7 +35,12 @@ namespace osu.Game.Overlays
private IAPIProvider api { get; set; }
public RankingsOverlay()
- : base(OverlayColourScheme.Green)
+ : base(OverlayColourScheme.Green, new RankingsOverlayHeader
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Depth = -float.MaxValue
+ })
{
Children = new Drawable[]
{
@@ -55,12 +59,7 @@ namespace osu.Game.Overlays
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- header = new RankingsOverlayHeader
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Depth = -float.MaxValue
- },
+ Header,
new Container
{
RelativeSizeAxes = Axes.X,
@@ -97,7 +96,7 @@ namespace osu.Game.Overlays
{
base.LoadComplete();
- header.Ruleset.BindTo(ruleset);
+ Header.Ruleset.BindTo(ruleset);
Country.BindValueChanged(_ =>
{
diff --git a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs
index e0163b5b0c..1990674aa9 100644
--- a/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs
+++ b/osu.Game/Overlays/SearchableList/SearchableListFilterControl.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Overlays.SearchableList
///
/// The amount of padding added to content (does not affect background or tab control strip).
///
- protected virtual float ContentHorizontalPadding => SearchableListOverlay.WIDTH_PADDING;
+ protected virtual float ContentHorizontalPadding => WaveOverlayContainer.WIDTH_PADDING;
protected SearchableListFilterControl()
{
diff --git a/osu.Game/Overlays/SearchableList/SearchableListHeader.cs b/osu.Game/Overlays/SearchableList/SearchableListHeader.cs
deleted file mode 100644
index 66fedf0a56..0000000000
--- a/osu.Game/Overlays/SearchableList/SearchableListHeader.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System;
-using osuTK;
-using osuTK.Graphics;
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Game.Graphics;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
-
-namespace osu.Game.Overlays.SearchableList
-{
- public abstract class SearchableListHeader : Container
- where T : struct, Enum
- {
- public readonly HeaderTabControl Tabs;
-
- protected abstract Color4 BackgroundColour { get; }
- protected abstract T DefaultTab { get; }
- protected abstract Drawable CreateHeaderText();
- protected abstract IconUsage Icon { get; }
-
- protected SearchableListHeader()
- {
- RelativeSizeAxes = Axes.X;
- Height = 90;
-
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = BackgroundColour,
- },
- new Container
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Left = SearchableListOverlay.WIDTH_PADDING, Right = SearchableListOverlay.WIDTH_PADDING },
- Children = new Drawable[]
- {
- new FillFlowContainer
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.BottomLeft,
- Position = new Vector2(-35f, 5f),
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Spacing = new Vector2(10f, 0f),
- Children = new[]
- {
- new SpriteIcon
- {
- Size = new Vector2(25),
- Icon = Icon,
- },
- CreateHeaderText(),
- },
- },
- Tabs = new HeaderTabControl
- {
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.BottomLeft,
- RelativeSizeAxes = Axes.X,
- },
- },
- },
- };
-
- Tabs.Current.Value = DefaultTab;
- Tabs.Current.TriggerChange();
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- Tabs.StripColour = colours.Green;
- }
- }
-}
diff --git a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
deleted file mode 100644
index 4ab2de06b6..0000000000
--- a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using System;
-using osuTK.Graphics;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input.Events;
-using osu.Game.Graphics.Backgrounds;
-using osu.Game.Graphics.Cursor;
-
-namespace osu.Game.Overlays.SearchableList
-{
- public abstract class SearchableListOverlay : FullscreenOverlay
- {
- public const float WIDTH_PADDING = 80;
-
- protected SearchableListOverlay(OverlayColourScheme colourScheme)
- : base(colourScheme)
- {
- }
- }
-
- public abstract class SearchableListOverlay : SearchableListOverlay
- where THeader : struct, Enum
- where TTab : struct, Enum
- where TCategory : struct, Enum
- {
- private readonly Container scrollContainer;
-
- protected readonly SearchableListHeader Header;
- protected readonly SearchableListFilterControl Filter;
- protected readonly FillFlowContainer ScrollFlow;
-
- protected abstract Color4 BackgroundColour { get; }
- protected abstract Color4 TrianglesColourLight { get; }
- protected abstract Color4 TrianglesColourDark { get; }
- protected abstract SearchableListHeader CreateHeader();
- protected abstract SearchableListFilterControl CreateFilterControl();
-
- protected SearchableListOverlay(OverlayColourScheme colourScheme)
- : base(colourScheme)
- {
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = BackgroundColour,
- },
- new Container
- {
- RelativeSizeAxes = Axes.Both,
- Masking = true,
- Children = new[]
- {
- new Triangles
- {
- RelativeSizeAxes = Axes.Both,
- TriangleScale = 5,
- ColourLight = TrianglesColourLight,
- ColourDark = TrianglesColourDark,
- },
- },
- },
- scrollContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
- Child = new OsuContextMenuContainer
- {
- RelativeSizeAxes = Axes.Both,
- Masking = true,
- Child = new OverlayScrollContainer
- {
- RelativeSizeAxes = Axes.Both,
- ScrollbarVisible = false,
- Child = ScrollFlow = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Padding = new MarginPadding { Horizontal = 10, Bottom = 50 },
- Direction = FillDirection.Vertical,
- },
- },
- },
- },
- new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Direction = FillDirection.Vertical,
- Children = new Drawable[]
- {
- Header = CreateHeader(),
- Filter = CreateFilterControl(),
- },
- },
- };
- }
-
- protected override void Update()
- {
- base.Update();
-
- scrollContainer.Padding = new MarginPadding { Top = Header.Height + Filter.Height };
- }
-
- protected override void OnFocus(FocusEvent e)
- {
- Filter.Search.TakeFocus();
- }
-
- protected override void PopIn()
- {
- base.PopIn();
-
- Filter.Search.HoldFocus = true;
- }
-
- protected override void PopOut()
- {
- base.PopOut();
-
- Filter.Search.HoldFocus = false;
- }
- }
-}
diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs
index aca507f20a..e5cebd28e2 100644
--- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs
+++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs
@@ -1,12 +1,14 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays.Settings.Sections.Gameplay;
using osu.Game.Rulesets;
using System.Linq;
using osu.Framework.Graphics.Sprites;
+using osu.Framework.Logging;
namespace osu.Game.Overlays.Settings.Sections
{
@@ -34,9 +36,17 @@ namespace osu.Game.Overlays.Settings.Sections
{
foreach (Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance()))
{
- SettingsSubsection section = ruleset.CreateSettings();
- if (section != null)
- Add(section);
+ try
+ {
+ SettingsSubsection section = ruleset.CreateSettings();
+
+ if (section != null)
+ Add(section);
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e, "Failed to load ruleset settings");
+ }
}
}
}
diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs
index 358f94b659..031ecaae46 100644
--- a/osu.Game/Overlays/Settings/Sidebar.cs
+++ b/osu.Game/Overlays/Settings/Sidebar.cs
@@ -4,22 +4,21 @@
using System;
using System.Linq;
using osu.Framework;
-using osuTK;
-using osuTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Framework.Threading;
using osu.Game.Graphics.Containers;
-using osu.Game.Overlays.Toolbar;
+using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Overlays.Settings
{
public class Sidebar : Container, IStateful
{
private readonly FillFlowContainer content;
- public const float DEFAULT_WIDTH = ToolbarButton.WIDTH;
+ public const float DEFAULT_WIDTH = Toolbar.Toolbar.HEIGHT * 1.4f;
public const int EXPANDED_WIDTH = 200;
public event Action StateChanged;
diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs
index bb84de5d3a..e1bcdbbaf0 100644
--- a/osu.Game/Overlays/SettingsOverlay.cs
+++ b/osu.Game/Overlays/SettingsOverlay.cs
@@ -13,8 +13,12 @@ using osu.Framework.Bindables;
namespace osu.Game.Overlays
{
- public class SettingsOverlay : SettingsPanel
+ public class SettingsOverlay : SettingsPanel, INamedOverlayComponent
{
+ public string IconTexture => "Icons/Hexacons/settings";
+ public string Title => "settings";
+ public string Description => "change the way osu! behaves";
+
protected override IEnumerable CreateSections() => new SettingsSection[]
{
new GeneralSection(),
@@ -30,7 +34,7 @@ namespace osu.Game.Overlays
private readonly List subPanels = new List();
- protected override Drawable CreateHeader() => new SettingsHeader("settings", "Change the way osu! behaves");
+ protected override Drawable CreateHeader() => new SettingsHeader(Title, Description);
protected override Drawable CreateFooter() => new SettingsFooter();
public SettingsOverlay()
diff --git a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs
index cde305fffd..0363873326 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Game.Graphics;
using osu.Game.Input.Bindings;
namespace osu.Game.Overlays.Toolbar
@@ -11,10 +10,6 @@ namespace osu.Game.Overlays.Toolbar
{
public ToolbarBeatmapListingButton()
{
- SetIcon(OsuIcon.ChevronDownCircle);
- TooltipMain = "Beatmap listing";
- TooltipSub = "Browse for new beatmaps";
-
Hotkey = GlobalAction.ToggleDirect;
}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs
index 0afc6642b2..49b9c62d85 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
@@ -25,8 +26,6 @@ namespace osu.Game.Overlays.Toolbar
{
public abstract class ToolbarButton : OsuClickableContainer, IKeyBindingHandler
{
- public const float WIDTH = Toolbar.HEIGHT * 1.4f;
-
protected GlobalAction? Hotkey { get; set; }
public void SetIcon(Drawable icon)
@@ -35,16 +34,14 @@ namespace osu.Game.Overlays.Toolbar
IconContainer.Show();
}
- public void SetIcon(IconUsage icon) => SetIcon(new SpriteIcon
- {
- Size = new Vector2(20),
- Icon = icon
- });
+ [Resolved]
+ private TextureStore textures { get; set; }
- public IconUsage Icon
- {
- set => SetIcon(value);
- }
+ public void SetIcon(string texture) =>
+ SetIcon(new Sprite
+ {
+ Texture = textures.Get(texture),
+ });
public string Text
{
@@ -82,7 +79,7 @@ namespace osu.Game.Overlays.Toolbar
protected ToolbarButton()
: base(HoverSampleSet.Loud)
{
- Width = WIDTH;
+ Width = Toolbar.HEIGHT;
RelativeSizeAxes = Axes.Y;
Children = new Drawable[]
@@ -116,7 +113,7 @@ namespace osu.Game.Overlays.Toolbar
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
- Size = new Vector2(20),
+ Size = new Vector2(26),
Alpha = 0,
},
DrawableText = new OsuSpriteText
diff --git a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs
index c88b418853..23f8b141b2 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs
@@ -2,19 +2,11 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Graphics.Sprites;
namespace osu.Game.Overlays.Toolbar
{
public class ToolbarChangelogButton : ToolbarOverlayToggleButton
{
- public ToolbarChangelogButton()
- {
- SetIcon(FontAwesome.Solid.Bullhorn);
- TooltipMain = "Changelog";
- TooltipSub = "Track recent dev updates in the osu! ecosystem";
- }
-
[BackgroundDependencyLoader(true)]
private void load(ChangelogOverlay changelog)
{
diff --git a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs
index dee4be0c1f..f9a66ae7bb 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Input.Bindings;
namespace osu.Game.Overlays.Toolbar
@@ -11,10 +10,6 @@ namespace osu.Game.Overlays.Toolbar
{
public ToolbarChatButton()
{
- SetIcon(FontAwesome.Solid.Comments);
- TooltipMain = "Chat";
- TooltipSub = "Join the real-time discussion";
-
Hotkey = GlobalAction.ToggleChat;
}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs
index 4845c9a99f..76fbd40d66 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs
@@ -1,7 +1,7 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Graphics.Sprites;
+using osu.Framework.Allocation;
using osu.Game.Input.Bindings;
namespace osu.Game.Overlays.Toolbar
@@ -10,11 +10,16 @@ namespace osu.Game.Overlays.Toolbar
{
public ToolbarHomeButton()
{
- Icon = FontAwesome.Solid.Home;
- TooltipMain = "Home";
- TooltipSub = "Return to the main menu";
-
+ Width *= 1.4f;
Hotkey = GlobalAction.Home;
}
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ TooltipMain = "home";
+ TooltipSub = "return to the main menu";
+ SetIcon("Icons/Hexacons/home");
+ }
}
}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs
index f9aa2de497..0f5e8e5456 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs
@@ -3,7 +3,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Input.Bindings;
namespace osu.Game.Overlays.Toolbar
@@ -14,10 +13,6 @@ namespace osu.Game.Overlays.Toolbar
public ToolbarMusicButton()
{
- Icon = FontAwesome.Solid.Music;
- TooltipMain = "Now playing";
- TooltipSub = "Manage the currently playing track";
-
Hotkey = GlobalAction.ToggleNowPlaying;
}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs
index 106c67a041..0ba2935c80 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs
@@ -2,19 +2,11 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Graphics.Sprites;
namespace osu.Game.Overlays.Toolbar
{
public class ToolbarNewsButton : ToolbarOverlayToggleButton
{
- public ToolbarNewsButton()
- {
- Icon = FontAwesome.Solid.Newspaper;
- TooltipMain = "News";
- TooltipSub = "Get up-to-date on community happenings";
- }
-
[BackgroundDependencyLoader(true)]
private void load(NewsOverlay news)
{
diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs
index a699fd907f..79d0fc74c1 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs
@@ -6,7 +6,6 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Input.Bindings;
@@ -25,10 +24,6 @@ namespace osu.Game.Overlays.Toolbar
public ToolbarNotificationButton()
{
- Icon = FontAwesome.Solid.Bars;
- TooltipMain = "Notifications";
- TooltipSub = "Waiting for 'ya";
-
Hotkey = GlobalAction.ToggleNotifications;
Add(countDisplay = new CountCircle
diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs
index 36387bb00d..0dea71cc08 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs
@@ -32,6 +32,13 @@ namespace osu.Game.Overlays.Toolbar
Action = stateContainer.ToggleVisibility;
overlayState.BindTo(stateContainer.State);
}
+
+ if (stateContainer is INamedOverlayComponent named)
+ {
+ TooltipMain = named.Title;
+ TooltipSub = named.Description;
+ SetIcon(named.IconTexture);
+ }
}
}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs
index c026ce99fe..22a01bcdb5 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs
@@ -2,19 +2,11 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Graphics.Sprites;
namespace osu.Game.Overlays.Toolbar
{
public class ToolbarRankingsButton : ToolbarOverlayToggleButton
{
- public ToolbarRankingsButton()
- {
- SetIcon(FontAwesome.Regular.ChartBar);
- TooltipMain = "Ranking";
- TooltipSub = "Find out who's the best right now";
- }
-
[BackgroundDependencyLoader(true)]
private void load(RankingsOverlay rankings)
{
diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
index 422bf00c6d..905d5b44c6 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Toolbar
},
ModeButtonLine = new Container
{
- Size = new Vector2(ToolbarButton.WIDTH, 3),
+ Size = new Vector2(Toolbar.HEIGHT, 3),
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft,
Masking = true,
diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs
index a5194ea752..564fd65719 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Toolbar
var rInstance = value.CreateInstance();
ruleset.TooltipMain = rInstance.Description;
- ruleset.TooltipSub = $"Play some {rInstance.Description}";
+ ruleset.TooltipSub = $"play some {rInstance.Description}";
ruleset.SetIcon(rInstance.CreateIcon());
}
@@ -65,12 +65,6 @@ namespace osu.Game.Overlays.Toolbar
Parent.Click();
return base.OnClick(e);
}
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- IconContainer.Scale *= 1.4f;
- }
}
}
}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs
index ed2a23ec2a..c53f4a55d9 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Input.Bindings;
namespace osu.Game.Overlays.Toolbar
@@ -11,10 +10,7 @@ namespace osu.Game.Overlays.Toolbar
{
public ToolbarSettingsButton()
{
- Icon = FontAwesome.Solid.Cog;
- TooltipMain = "Settings";
- TooltipSub = "Change your settings";
-
+ Width *= 1.4f;
Hotkey = GlobalAction.ToggleSettings;
}
diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs
index 6faa58c559..e62c7bc807 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Input.Bindings;
namespace osu.Game.Overlays.Toolbar
@@ -11,10 +10,6 @@ namespace osu.Game.Overlays.Toolbar
{
public ToolbarSocialButton()
{
- Icon = FontAwesome.Solid.Users;
- TooltipMain = "Friends";
- TooltipSub = "Interact with those close to you";
-
Hotkey = GlobalAction.ToggleSocial;
}
diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs
index b4c8a2d3ca..2b316c0e34 100644
--- a/osu.Game/Overlays/UserProfileOverlay.cs
+++ b/osu.Game/Overlays/UserProfileOverlay.cs
@@ -17,19 +17,18 @@ using osuTK;
namespace osu.Game.Overlays
{
- public class UserProfileOverlay : FullscreenOverlay
+ public class UserProfileOverlay : FullscreenOverlay
{
private ProfileSection lastSection;
private ProfileSection[] sections;
private GetUserRequest userReq;
- protected ProfileHeader Header;
private ProfileSectionsContainer sectionsContainer;
private ProfileSectionTabControl tabs;
public const float CONTENT_X_MARGIN = 70;
public UserProfileOverlay()
- : base(OverlayColourScheme.Pink)
+ : base(OverlayColourScheme.Pink, new ProfileHeader())
{
}
@@ -45,6 +44,9 @@ namespace osu.Game.Overlays
if (user.Id == Header?.User.Value?.Id)
return;
+ if (sectionsContainer != null)
+ sectionsContainer.ExpandableHeader = null;
+
userReq?.Cancel();
Clear();
lastSection = null;
@@ -77,7 +79,7 @@ namespace osu.Game.Overlays
Add(sectionsContainer = new ProfileSectionsContainer
{
- ExpandableHeader = Header = new ProfileHeader(),
+ ExpandableHeader = Header,
FixedHeader = tabs,
HeaderBackground = new Box
{
diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs
index 5c87096dd4..d0fa9987d5 100644
--- a/osu.Game/Overlays/WaveOverlayContainer.cs
+++ b/osu.Game/Overlays/WaveOverlayContainer.cs
@@ -14,6 +14,8 @@ namespace osu.Game.Overlays
protected override bool BlockNonPositionalInput => true;
protected override Container Content => Waves;
+ public const float WIDTH_PADDING = 80;
+
protected override bool StartHidden => true;
protected WaveOverlayContainer()
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index f134db1ffe..b9cc054ed3 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
@@ -13,6 +14,7 @@ using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
@@ -92,9 +94,18 @@ namespace osu.Game.Rulesets.Edit
Name = "Sidebar",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Right = 10 },
+ Spacing = new Vector2(10),
Children = new Drawable[]
{
- new ToolboxGroup { Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X } }
+ new ToolboxGroup("toolbox") { Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X } },
+ new ToolboxGroup("toggles")
+ {
+ ChildrenEnumerable = Toggles.Select(b => new SettingsCheckbox
+ {
+ Bindable = b,
+ LabelText = b?.Description ?? "unknown"
+ })
+ }
}
},
new Container
@@ -126,7 +137,7 @@ namespace osu.Game.Rulesets.Edit
toolboxCollection.Items = CompositionTools
.Prepend(new SelectTool())
- .Select(t => new RadioButton(t.Name, () => toolSelected(t)))
+ .Select(t => new RadioButton(t.Name, () => toolSelected(t), t.CreateIcon))
.ToList();
setSelectTool();
@@ -156,6 +167,12 @@ namespace osu.Game.Rulesets.Edit
///
protected abstract IReadOnlyList CompositionTools { get; }
+ ///
+ /// A collection of toggles which will be displayed to the user.
+ /// The display name will be decided by .
+ ///
+ protected virtual IEnumerable Toggles => Enumerable.Empty();
+
///
/// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic.
///
diff --git a/osu.Game/Rulesets/Edit/ToolboxGroup.cs b/osu.Game/Rulesets/Edit/ToolboxGroup.cs
index eabb834616..22b2b05657 100644
--- a/osu.Game/Rulesets/Edit/ToolboxGroup.cs
+++ b/osu.Game/Rulesets/Edit/ToolboxGroup.cs
@@ -8,9 +8,8 @@ namespace osu.Game.Rulesets.Edit
{
public class ToolboxGroup : PlayerSettingsGroup
{
- protected override string Title => "toolbox";
-
- public ToolboxGroup()
+ public ToolboxGroup(string title)
+ : base(title)
{
RelativeSizeAxes = Axes.X;
Width = 1;
diff --git a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs
index 0631031302..0a01ac4320 100644
--- a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs
+++ b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Graphics;
+
namespace osu.Game.Rulesets.Edit.Tools
{
public abstract class HitObjectCompositionTool
@@ -14,6 +16,8 @@ namespace osu.Game.Rulesets.Edit.Tools
public abstract PlacementBlueprint CreatePlacementBlueprint();
+ public virtual Drawable CreateIcon() => null;
+
public override string ToString() => Name;
}
}
diff --git a/osu.Game/Rulesets/Edit/Tools/SelectTool.cs b/osu.Game/Rulesets/Edit/Tools/SelectTool.cs
index b96eeb0790..c050766b23 100644
--- a/osu.Game/Rulesets/Edit/Tools/SelectTool.cs
+++ b/osu.Game/Rulesets/Edit/Tools/SelectTool.cs
@@ -1,6 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+
namespace osu.Game.Rulesets.Edit.Tools
{
public class SelectTool : HitObjectCompositionTool
@@ -10,6 +13,8 @@ namespace osu.Game.Rulesets.Edit.Tools
{
}
+ public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.MousePointer };
+
public override PlacementBlueprint CreatePlacementBlueprint() => null;
}
}
diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs
index 52ffa0ad2a..b8dc7a2661 100644
--- a/osu.Game/Rulesets/Mods/Mod.cs
+++ b/osu.Game/Rulesets/Mods/Mod.cs
@@ -9,6 +9,7 @@ using System.Reflection;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
+using osu.Framework.Testing;
using osu.Game.Configuration;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets.UI;
@@ -18,6 +19,7 @@ namespace osu.Game.Rulesets.Mods
///
/// The base class for gameplay modifiers.
///
+ [ExcludeFromDynamicCompile]
public abstract class Mod : IMod, IJsonSerializable
{
///
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index 3a7f433a37..915544d010 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -23,10 +23,12 @@ using osu.Game.Scoring;
using osu.Game.Skinning;
using osu.Game.Users;
using JetBrains.Annotations;
+using osu.Framework.Testing;
using osu.Game.Screens.Ranking.Statistics;
namespace osu.Game.Rulesets
{
+ [ExcludeFromDynamicCompile]
public abstract class Ruleset
{
public RulesetInfo RulesetInfo { get; internal set; }
diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs
index 2e32b96084..d5aca8c650 100644
--- a/osu.Game/Rulesets/RulesetInfo.cs
+++ b/osu.Game/Rulesets/RulesetInfo.cs
@@ -5,9 +5,11 @@ using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Newtonsoft.Json;
+using osu.Framework.Testing;
namespace osu.Game.Rulesets
{
+ [ExcludeFromDynamicCompile]
public class RulesetInfo : IEquatable
{
public int? ID { get; set; }
diff --git a/osu.Game/Screens/Edit/Components/Menus/ScreenSelectionTabControl.cs b/osu.Game/Screens/Edit/Components/Menus/ScreenSelectionTabControl.cs
index 089da4f222..b8bc5cdf36 100644
--- a/osu.Game/Screens/Edit/Components/Menus/ScreenSelectionTabControl.cs
+++ b/osu.Game/Screens/Edit/Components/Menus/ScreenSelectionTabControl.cs
@@ -32,8 +32,6 @@ namespace osu.Game.Screens.Edit.Components.Menus
Height = 1,
Colour = Color4.White.Opacity(0.2f),
});
-
- Current.Value = EditorScreenMode.Compose;
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs
index 7be91f4e8e..0cf7b83f3b 100644
--- a/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs
+++ b/osu.Game/Screens/Edit/Components/RadioButtons/DrawableRadioButton.cs
@@ -5,7 +5,6 @@ using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
@@ -29,7 +28,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
private Color4 selectedBackgroundColour;
private Color4 selectedBubbleColour;
- private readonly Drawable bubble;
+ private Drawable icon;
private readonly RadioButton button;
public DrawableRadioButton(RadioButton button)
@@ -40,19 +39,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
Action = button.Select;
RelativeSizeAxes = Axes.X;
-
- bubble = new CircularContainer
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fit,
- Scale = new Vector2(0.5f),
- X = 10,
- Masking = true,
- Blending = BlendingParameters.Additive,
- Child = new Box { RelativeSizeAxes = Axes.Both }
- };
}
[BackgroundDependencyLoader]
@@ -73,7 +59,14 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
Colour = Color4.Black.Opacity(0.5f)
};
- Add(bubble);
+ Add(icon = (button.CreateIcon?.Invoke() ?? new Circle()).With(b =>
+ {
+ b.Blending = BlendingParameters.Additive;
+ b.Anchor = Anchor.CentreLeft;
+ b.Origin = Anchor.CentreLeft;
+ b.Size = new Vector2(20);
+ b.X = 10;
+ }));
}
protected override void LoadComplete()
@@ -96,7 +89,7 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
return;
BackgroundColour = button.Selected.Value ? selectedBackgroundColour : defaultBackgroundColour;
- bubble.Colour = button.Selected.Value ? selectedBubbleColour : defaultBubbleColour;
+ icon.Colour = button.Selected.Value ? selectedBubbleColour : defaultBubbleColour;
}
protected override SpriteText CreateText() => new OsuSpriteText
diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs
index b515d7c8bd..a7b0fb05e3 100644
--- a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs
+++ b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs
@@ -3,6 +3,7 @@
using System;
using osu.Framework.Bindables;
+using osu.Framework.Graphics;
namespace osu.Game.Screens.Edit.Components.RadioButtons
{
@@ -19,11 +20,17 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
///
public object Item;
+ ///
+ /// A function which creates a drawable icon to represent this item. If null, a sane default should be used.
+ ///
+ public readonly Func CreateIcon;
+
private readonly Action action;
- public RadioButton(object item, Action action)
+ public RadioButton(object item, Action action, Func createIcon = null)
{
Item = item;
+ CreateIcon = createIcon;
this.action = action;
Selected = new BindableBool();
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
index fcff672045..865e225645 100644
--- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
@@ -61,7 +61,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
AddRangeInternal(new[]
{
- DragBox = CreateDragBox(select),
+ DragBox = CreateDragBox(selectBlueprintsFromDragRectangle),
selectionHandler,
SelectionBlueprints = CreateSelectionBlueprintContainer(),
selectionHandler.CreateProxy(),
@@ -326,7 +326,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// Select all masks in a given rectangle selection area.
///
/// The rectangle to perform a selection on in screen-space coordinates.
- private void select(RectangleF rect)
+ private void selectBlueprintsFromDragRectangle(RectangleF rect)
{
foreach (var blueprint in SelectionBlueprints)
{
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index e178459d5c..23eb704920 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -2,38 +2,40 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osuTK.Graphics;
-using osu.Framework.Screens;
+using System.Collections.Generic;
+using osu.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Graphics;
-using osu.Game.Screens.Edit.Components.Timelines.Summary;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input.Events;
-using osu.Framework.Platform;
-using osu.Framework.Timing;
-using osu.Game.Graphics.UserInterface;
-using osu.Game.Screens.Edit.Components;
-using osu.Game.Screens.Edit.Components.Menus;
-using osu.Game.Screens.Edit.Design;
-using osuTK.Input;
-using System.Collections.Generic;
-using osu.Framework;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
+using osu.Framework.Input.Events;
using osu.Framework.Logging;
+using osu.Framework.Platform;
+using osu.Framework.Screens;
+using osu.Framework.Timing;
using osu.Game.Beatmaps;
+using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
+using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings;
+using osu.Game.Online.API;
+using osu.Game.Overlays;
using osu.Game.Rulesets.Edit;
+using osu.Game.Screens.Edit.Components;
+using osu.Game.Screens.Edit.Components.Menus;
+using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Game.Screens.Edit.Compose;
+using osu.Game.Screens.Edit.Design;
using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Edit.Timing;
using osu.Game.Screens.Play;
using osu.Game.Users;
+using osuTK.Graphics;
+using osuTK.Input;
namespace osu.Game.Screens.Edit
{
@@ -50,9 +52,18 @@ namespace osu.Game.Screens.Edit
public override bool AllowRateAdjustments => false;
+ protected bool HasUnsavedChanges => lastSavedHash != changeHandler.CurrentStateHash;
+
[Resolved]
private BeatmapManager beatmapManager { get; set; }
+ [Resolved(canBeNull: true)]
+ private DialogOverlay dialogOverlay { get; set; }
+
+ private bool exitConfirmed;
+
+ private string lastSavedHash;
+
private Box bottomBackground;
private Container screenContainer;
@@ -72,6 +83,9 @@ namespace osu.Game.Screens.Edit
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+ [Resolved]
+ private IAPIProvider api { get; set; }
+
[BackgroundDependencyLoader]
private void load(OsuColour colours, GameHost host)
{
@@ -89,6 +103,14 @@ namespace osu.Game.Screens.Edit
// todo: remove caching of this and consume via editorBeatmap?
dependencies.Cache(beatDivisor);
+ bool isNewBeatmap = false;
+
+ if (Beatmap.Value is DummyWorkingBeatmap)
+ {
+ isNewBeatmap = true;
+ Beatmap.Value = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value);
+ }
+
try
{
playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset);
@@ -101,19 +123,20 @@ namespace osu.Game.Screens.Edit
return;
}
- AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap));
+ AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, Beatmap.Value.Skin));
dependencies.CacheAs(editorBeatmap);
-
changeHandler = new EditorChangeHandler(editorBeatmap);
dependencies.CacheAs(changeHandler);
+ updateLastSavedHash();
+
EditorMenuBar menuBar;
OsuMenuItem undoMenuItem;
OsuMenuItem redoMenuItem;
var fileMenuItems = new List