mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 15:53:21 +08:00
Merge branch 'master' into import-early-checksum-abort
This commit is contained in:
commit
92fcf90768
@ -1,3 +1,3 @@
|
|||||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||||
<Realm />
|
<Realm DisableAnalytics="true" />
|
||||||
</Weavers>
|
</Weavers>
|
@ -52,7 +52,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.618.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.618.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.622.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.628.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Rulesets.Mania.Configuration;
|
using osu.Game.Rulesets.Mania.Configuration;
|
||||||
@ -47,7 +48,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
private class TimeSlider : OsuSliderBar<double>
|
private class TimeSlider : OsuSliderBar<double>
|
||||||
{
|
{
|
||||||
public override string TooltipText => Current.Value.ToString("N0") + "ms";
|
public override LocalisableString TooltipText => Current.Value.ToString(@"N0") + "ms";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics.Cursor;
|
|||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
@ -283,6 +284,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TooltipText => ControlPoint.Type.Value.ToString() ?? string.Empty;
|
public LocalisableString TooltipText => ControlPoint.Type.Value.ToString() ?? string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
241
osu.Game.Tests/Editing/Checks/CheckFewHitsoundsTest.cs
Normal file
241
osu.Game.Tests/Editing/Checks/CheckFewHitsoundsTest.cs
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Edit.Checks;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Editing.Checks
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class CheckFewHitsoundsTest
|
||||||
|
{
|
||||||
|
private CheckFewHitsounds check;
|
||||||
|
|
||||||
|
private List<HitSampleInfo> notHitsounded;
|
||||||
|
private List<HitSampleInfo> hitsounded;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
check = new CheckFewHitsounds();
|
||||||
|
notHitsounded = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) };
|
||||||
|
hitsounded = new List<HitSampleInfo>
|
||||||
|
{
|
||||||
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL),
|
||||||
|
new HitSampleInfo(HitSampleInfo.HIT_FINISH)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHitsounded()
|
||||||
|
{
|
||||||
|
var hitObjects = new List<HitObject>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
var samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) };
|
||||||
|
|
||||||
|
if ((i + 1) % 2 == 0)
|
||||||
|
samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP));
|
||||||
|
if ((i + 1) % 3 == 0)
|
||||||
|
samples.Add(new HitSampleInfo(HitSampleInfo.HIT_WHISTLE));
|
||||||
|
if ((i + 1) % 4 == 0)
|
||||||
|
samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH));
|
||||||
|
|
||||||
|
hitObjects.Add(new HitCircle { StartTime = 1000 * i, Samples = samples });
|
||||||
|
}
|
||||||
|
|
||||||
|
assertOk(hitObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHitsoundedWithBreak()
|
||||||
|
{
|
||||||
|
var hitObjects = new List<HitObject>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; ++i)
|
||||||
|
{
|
||||||
|
var samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) };
|
||||||
|
|
||||||
|
if ((i + 1) % 2 == 0)
|
||||||
|
samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP));
|
||||||
|
if ((i + 1) % 3 == 0)
|
||||||
|
samples.Add(new HitSampleInfo(HitSampleInfo.HIT_WHISTLE));
|
||||||
|
if ((i + 1) % 4 == 0)
|
||||||
|
samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH));
|
||||||
|
// Leaves a gap in which no hitsounds exist or can be added, and so shouldn't be an issue.
|
||||||
|
if (i > 8 && i < 24)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hitObjects.Add(new HitCircle { StartTime = 1000 * i, Samples = samples });
|
||||||
|
}
|
||||||
|
|
||||||
|
assertOk(hitObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLightlyHitsounded()
|
||||||
|
{
|
||||||
|
var hitObjects = new List<HitObject>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 30; ++i)
|
||||||
|
{
|
||||||
|
var samples = i % 8 == 0 ? hitsounded : notHitsounded;
|
||||||
|
|
||||||
|
hitObjects.Add(new HitCircle { StartTime = 1000 * i, Samples = samples });
|
||||||
|
}
|
||||||
|
|
||||||
|
assertLongPeriodNegligible(hitObjects, count: 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRarelyHitsounded()
|
||||||
|
{
|
||||||
|
var hitObjects = new List<HitObject>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 30; ++i)
|
||||||
|
{
|
||||||
|
var samples = (i == 0 || i == 15) ? hitsounded : notHitsounded;
|
||||||
|
|
||||||
|
hitObjects.Add(new HitCircle { StartTime = 1000 * i, Samples = samples });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should prompt one warning between 1st and 16th, and another between 16th and 31st.
|
||||||
|
assertLongPeriodWarning(hitObjects, count: 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestExtremelyRarelyHitsounded()
|
||||||
|
{
|
||||||
|
var hitObjects = new List<HitObject>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 80; ++i)
|
||||||
|
{
|
||||||
|
var samples = i == 40 ? hitsounded : notHitsounded;
|
||||||
|
|
||||||
|
hitObjects.Add(new HitCircle { StartTime = 1000 * i, Samples = samples });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should prompt one problem between 1st and 41st, and another between 41st and 81st.
|
||||||
|
assertLongPeriodProblem(hitObjects, count: 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNotHitsounded()
|
||||||
|
{
|
||||||
|
var hitObjects = new List<HitObject>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 20; ++i)
|
||||||
|
hitObjects.Add(new HitCircle { StartTime = 1000 * i, Samples = notHitsounded });
|
||||||
|
|
||||||
|
assertNoHitsounds(hitObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNestedObjectsHitsounded()
|
||||||
|
{
|
||||||
|
var ticks = new List<HitObject>();
|
||||||
|
for (int i = 1; i < 16; ++i)
|
||||||
|
ticks.Add(new SliderTick { StartTime = 1000 * i, Samples = hitsounded });
|
||||||
|
|
||||||
|
var nested = new MockNestableHitObject(ticks.ToList(), 0, 16000)
|
||||||
|
{
|
||||||
|
Samples = hitsounded
|
||||||
|
};
|
||||||
|
nested.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertOk(new List<HitObject> { nested });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNestedObjectsRarelyHitsounded()
|
||||||
|
{
|
||||||
|
var ticks = new List<HitObject>();
|
||||||
|
for (int i = 1; i < 16; ++i)
|
||||||
|
ticks.Add(new SliderTick { StartTime = 1000 * i, Samples = i == 0 ? hitsounded : notHitsounded });
|
||||||
|
|
||||||
|
var nested = new MockNestableHitObject(ticks.ToList(), 0, 16000)
|
||||||
|
{
|
||||||
|
Samples = hitsounded
|
||||||
|
};
|
||||||
|
nested.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertLongPeriodWarning(new List<HitObject> { nested });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestConcurrentObjects()
|
||||||
|
{
|
||||||
|
var hitObjects = new List<HitObject>();
|
||||||
|
|
||||||
|
var ticks = new List<HitObject>();
|
||||||
|
for (int i = 1; i < 10; ++i)
|
||||||
|
ticks.Add(new SliderTick { StartTime = 5000 * i, Samples = hitsounded });
|
||||||
|
|
||||||
|
var nested = new MockNestableHitObject(ticks.ToList(), 0, 50000)
|
||||||
|
{
|
||||||
|
Samples = notHitsounded
|
||||||
|
};
|
||||||
|
nested.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
hitObjects.Add(nested);
|
||||||
|
|
||||||
|
for (int i = 1; i <= 6; ++i)
|
||||||
|
hitObjects.Add(new HitCircle { StartTime = 10000 * i, Samples = notHitsounded });
|
||||||
|
|
||||||
|
assertOk(hitObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertOk(List<HitObject> hitObjects)
|
||||||
|
{
|
||||||
|
Assert.That(check.Run(getContext(hitObjects)), Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertLongPeriodProblem(List<HitObject> hitObjects, int count = 1)
|
||||||
|
{
|
||||||
|
var issues = check.Run(getContext(hitObjects)).ToList();
|
||||||
|
|
||||||
|
Assert.That(issues, Has.Count.EqualTo(count));
|
||||||
|
Assert.That(issues.All(issue => issue.Template is CheckFewHitsounds.IssueTemplateLongPeriodProblem));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertLongPeriodWarning(List<HitObject> hitObjects, int count = 1)
|
||||||
|
{
|
||||||
|
var issues = check.Run(getContext(hitObjects)).ToList();
|
||||||
|
|
||||||
|
Assert.That(issues, Has.Count.EqualTo(count));
|
||||||
|
Assert.That(issues.All(issue => issue.Template is CheckFewHitsounds.IssueTemplateLongPeriodWarning));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertLongPeriodNegligible(List<HitObject> hitObjects, int count = 1)
|
||||||
|
{
|
||||||
|
var issues = check.Run(getContext(hitObjects)).ToList();
|
||||||
|
|
||||||
|
Assert.That(issues, Has.Count.EqualTo(count));
|
||||||
|
Assert.That(issues.All(issue => issue.Template is CheckFewHitsounds.IssueTemplateLongPeriodNegligible));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNoHitsounds(List<HitObject> hitObjects)
|
||||||
|
{
|
||||||
|
var issues = check.Run(getContext(hitObjects)).ToList();
|
||||||
|
|
||||||
|
Assert.That(issues, Has.Count.EqualTo(1));
|
||||||
|
Assert.That(issues.Any(issue => issue.Template is CheckFewHitsounds.IssueTemplateNoHitsounds));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeatmapVerifierContext getContext(List<HitObject> hitObjects)
|
||||||
|
{
|
||||||
|
var beatmap = new Beatmap<HitObject> { HitObjects = hitObjects };
|
||||||
|
|
||||||
|
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
289
osu.Game.Tests/Editing/Checks/CheckMutedObjectsTest.cs
Normal file
289
osu.Game.Tests/Editing/Checks/CheckMutedObjectsTest.cs
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Edit.Checks;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Editing.Checks
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class CheckMutedObjectsTest
|
||||||
|
{
|
||||||
|
private CheckMutedObjects check;
|
||||||
|
private ControlPointInfo cpi;
|
||||||
|
|
||||||
|
private const int volume_regular = 50;
|
||||||
|
private const int volume_low = 15;
|
||||||
|
private const int volume_muted = 5;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
check = new CheckMutedObjects();
|
||||||
|
|
||||||
|
cpi = new ControlPointInfo();
|
||||||
|
cpi.Add(0, new SampleControlPoint { SampleVolume = volume_regular });
|
||||||
|
cpi.Add(1000, new SampleControlPoint { SampleVolume = volume_low });
|
||||||
|
cpi.Add(2000, new SampleControlPoint { SampleVolume = volume_muted });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNormalControlPointVolume()
|
||||||
|
{
|
||||||
|
var hitcircle = new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
||||||
|
};
|
||||||
|
hitcircle.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertOk(new List<HitObject> { hitcircle });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLowControlPointVolume()
|
||||||
|
{
|
||||||
|
var hitcircle = new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 1000,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
||||||
|
};
|
||||||
|
hitcircle.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertLowVolume(new List<HitObject> { hitcircle });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMutedControlPointVolume()
|
||||||
|
{
|
||||||
|
var hitcircle = new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 2000,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
||||||
|
};
|
||||||
|
hitcircle.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertMuted(new List<HitObject> { hitcircle });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNormalSampleVolume()
|
||||||
|
{
|
||||||
|
// The sample volume should take precedence over the control point volume.
|
||||||
|
var hitcircle = new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 2000,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: volume_regular) }
|
||||||
|
};
|
||||||
|
hitcircle.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertOk(new List<HitObject> { hitcircle });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLowSampleVolume()
|
||||||
|
{
|
||||||
|
var hitcircle = new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 2000,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: volume_low) }
|
||||||
|
};
|
||||||
|
hitcircle.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertLowVolume(new List<HitObject> { hitcircle });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMutedSampleVolume()
|
||||||
|
{
|
||||||
|
var hitcircle = new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: volume_muted) }
|
||||||
|
};
|
||||||
|
hitcircle.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertMuted(new List<HitObject> { hitcircle });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNormalSampleVolumeSlider()
|
||||||
|
{
|
||||||
|
var sliderHead = new SliderHeadCircle
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
||||||
|
};
|
||||||
|
sliderHead.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
var sliderTick = new SliderTick
|
||||||
|
{
|
||||||
|
StartTime = 250,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo("slidertick", volume: volume_muted) } // Should be fine.
|
||||||
|
};
|
||||||
|
sliderTick.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
var slider = new MockNestableHitObject(new List<HitObject> { sliderHead, sliderTick, }, startTime: 0, endTime: 500)
|
||||||
|
{
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
||||||
|
};
|
||||||
|
slider.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertOk(new List<HitObject> { slider });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMutedSampleVolumeSliderHead()
|
||||||
|
{
|
||||||
|
var sliderHead = new SliderHeadCircle
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: volume_muted) }
|
||||||
|
};
|
||||||
|
sliderHead.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
var sliderTick = new SliderTick
|
||||||
|
{
|
||||||
|
StartTime = 250,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo("slidertick") }
|
||||||
|
};
|
||||||
|
sliderTick.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
var slider = new MockNestableHitObject(new List<HitObject> { sliderHead, sliderTick, }, startTime: 0, endTime: 500)
|
||||||
|
{
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) } // Applies to the tail.
|
||||||
|
};
|
||||||
|
slider.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertMuted(new List<HitObject> { slider });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMutedSampleVolumeSliderTail()
|
||||||
|
{
|
||||||
|
var sliderHead = new SliderHeadCircle
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
||||||
|
};
|
||||||
|
sliderHead.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
var sliderTick = new SliderTick
|
||||||
|
{
|
||||||
|
StartTime = 250,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo("slidertick") }
|
||||||
|
};
|
||||||
|
sliderTick.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
var slider = new MockNestableHitObject(new List<HitObject> { sliderHead, sliderTick, }, startTime: 0, endTime: 2500)
|
||||||
|
{
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: volume_muted) } // Applies to the tail.
|
||||||
|
};
|
||||||
|
slider.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertMutedPassive(new List<HitObject> { slider });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMutedControlPointVolumeSliderHead()
|
||||||
|
{
|
||||||
|
var sliderHead = new SliderHeadCircle
|
||||||
|
{
|
||||||
|
StartTime = 2000,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
||||||
|
};
|
||||||
|
sliderHead.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
var sliderTick = new SliderTick
|
||||||
|
{
|
||||||
|
StartTime = 2250,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo("slidertick") }
|
||||||
|
};
|
||||||
|
sliderTick.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
var slider = new MockNestableHitObject(new List<HitObject> { sliderHead, sliderTick, }, startTime: 2000, endTime: 2500)
|
||||||
|
{
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: volume_regular) }
|
||||||
|
};
|
||||||
|
slider.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertMuted(new List<HitObject> { slider });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMutedControlPointVolumeSliderTail()
|
||||||
|
{
|
||||||
|
var sliderHead = new SliderHeadCircle
|
||||||
|
{
|
||||||
|
StartTime = 0,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
||||||
|
};
|
||||||
|
sliderHead.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
var sliderTick = new SliderTick
|
||||||
|
{
|
||||||
|
StartTime = 250,
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo("slidertick") }
|
||||||
|
};
|
||||||
|
sliderTick.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
// Ends after the 5% control point.
|
||||||
|
var slider = new MockNestableHitObject(new List<HitObject> { sliderHead, sliderTick, }, startTime: 0, endTime: 2500)
|
||||||
|
{
|
||||||
|
Samples = new List<HitSampleInfo> { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
||||||
|
};
|
||||||
|
slider.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
assertMutedPassive(new List<HitObject> { slider });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertOk(List<HitObject> hitObjects)
|
||||||
|
{
|
||||||
|
Assert.That(check.Run(getContext(hitObjects)), Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertLowVolume(List<HitObject> hitObjects, int count = 1)
|
||||||
|
{
|
||||||
|
var issues = check.Run(getContext(hitObjects)).ToList();
|
||||||
|
|
||||||
|
Assert.That(issues, Has.Count.EqualTo(count));
|
||||||
|
Assert.That(issues.All(issue => issue.Template is CheckMutedObjects.IssueTemplateLowVolumeActive));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertMuted(List<HitObject> hitObjects, int count = 1)
|
||||||
|
{
|
||||||
|
var issues = check.Run(getContext(hitObjects)).ToList();
|
||||||
|
|
||||||
|
Assert.That(issues, Has.Count.EqualTo(count));
|
||||||
|
Assert.That(issues.All(issue => issue.Template is CheckMutedObjects.IssueTemplateMutedActive));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertMutedPassive(List<HitObject> hitObjects)
|
||||||
|
{
|
||||||
|
var issues = check.Run(getContext(hitObjects)).ToList();
|
||||||
|
|
||||||
|
Assert.That(issues, Has.Count.EqualTo(1));
|
||||||
|
Assert.That(issues.Any(issue => issue.Template is CheckMutedObjects.IssueTemplateMutedPassive));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeatmapVerifierContext getContext(List<HitObject> hitObjects)
|
||||||
|
{
|
||||||
|
var beatmap = new Beatmap<HitObject>
|
||||||
|
{
|
||||||
|
ControlPointInfo = cpi,
|
||||||
|
HitObjects = hitObjects
|
||||||
|
};
|
||||||
|
|
||||||
|
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
osu.Game.Tests/Editing/Checks/MockNestableHitObject.cs
Normal file
36
osu.Game.Tests/Editing/Checks/MockNestableHitObject.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Editing.Checks
|
||||||
|
{
|
||||||
|
public sealed class MockNestableHitObject : HitObject, IHasDuration
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<HitObject> toBeNested;
|
||||||
|
|
||||||
|
public MockNestableHitObject(IEnumerable<HitObject> toBeNested, double startTime, double endTime)
|
||||||
|
{
|
||||||
|
this.toBeNested = toBeNested;
|
||||||
|
StartTime = startTime;
|
||||||
|
EndTime = endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
foreach (var hitObject in toBeNested)
|
||||||
|
AddNested(hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double EndTime { get; }
|
||||||
|
|
||||||
|
public double Duration
|
||||||
|
{
|
||||||
|
get => EndTime - StartTime;
|
||||||
|
set => throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.OpenGL.Textures;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Tests.Testing;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Rulesets
|
||||||
|
{
|
||||||
|
public class TestSceneRulesetSkinProvidingContainer : OsuTestScene
|
||||||
|
{
|
||||||
|
private SkinRequester requester;
|
||||||
|
|
||||||
|
protected override Ruleset CreateRuleset() => new TestSceneRulesetDependencies.TestRuleset();
|
||||||
|
|
||||||
|
[Cached(typeof(ISkinSource))]
|
||||||
|
private readonly ISkinSource testSource = new TestSkinProvider();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEarlyAddedSkinRequester()
|
||||||
|
{
|
||||||
|
Texture textureOnLoad = null;
|
||||||
|
|
||||||
|
AddStep("setup provider", () =>
|
||||||
|
{
|
||||||
|
var rulesetSkinProvider = new RulesetSkinProvidingContainer(Ruleset.Value.CreateInstance(), Beatmap.Value.Beatmap, Beatmap.Value.Skin);
|
||||||
|
|
||||||
|
rulesetSkinProvider.Add(requester = new SkinRequester());
|
||||||
|
|
||||||
|
requester.OnLoadAsync += () => textureOnLoad = requester.GetTexture(TestSkinProvider.TEXTURE_NAME);
|
||||||
|
|
||||||
|
Child = rulesetSkinProvider;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("requester got correct initial texture", () => textureOnLoad != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SkinRequester : Drawable, ISkin
|
||||||
|
{
|
||||||
|
private ISkinSource skin;
|
||||||
|
|
||||||
|
public event Action OnLoadAsync;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin)
|
||||||
|
{
|
||||||
|
this.skin = skin;
|
||||||
|
|
||||||
|
OnLoadAsync?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Drawable GetDrawableComponent(ISkinComponent component) => skin.GetDrawableComponent(component);
|
||||||
|
|
||||||
|
public Texture GetTexture(string componentName, WrapMode wrapModeS = default, WrapMode wrapModeT = default) => skin.GetTexture(componentName);
|
||||||
|
|
||||||
|
public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
|
||||||
|
|
||||||
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => skin.GetConfig<TLookup, TValue>(lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestSkinProvider : ISkinSource
|
||||||
|
{
|
||||||
|
public const string TEXTURE_NAME = "some-texture";
|
||||||
|
|
||||||
|
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => componentName == TEXTURE_NAME ? Texture.WhitePixel : null;
|
||||||
|
|
||||||
|
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public event Action SourceChanged
|
||||||
|
{
|
||||||
|
add { }
|
||||||
|
remove { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => lookupFunction(this) ? this : null;
|
||||||
|
|
||||||
|
public IEnumerable<ISkin> AllSources => new[] { this };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Testing
|
|||||||
Dependencies.Get<TestRulesetConfigManager>() != null);
|
Dependencies.Get<TestRulesetConfigManager>() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestRuleset : Ruleset
|
public class TestRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override string Description => string.Empty;
|
public override string Description => string.Empty;
|
||||||
public override string ShortName => string.Empty;
|
public override string ShortName => string.Empty;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -14,6 +15,8 @@ using osu.Game.Online.API.Requests.Responses;
|
|||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.BeatmapListing;
|
using osu.Game.Overlays.BeatmapListing;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
namespace osu.Game.Tests.Visual.Online
|
||||||
{
|
{
|
||||||
@ -23,6 +26,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
private BeatmapListingOverlay overlay;
|
private BeatmapListingOverlay overlay;
|
||||||
|
|
||||||
|
private BeatmapListingSearchControl searchControl => overlay.ChildrenOfType<BeatmapListingSearchControl>().Single();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
@ -39,6 +44,16 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AddStep("initialize dummy", () =>
|
||||||
|
{
|
||||||
|
// non-supporter user
|
||||||
|
((DummyAPIAccess)API).LocalUser.Value = new User
|
||||||
|
{
|
||||||
|
Username = "TestBot",
|
||||||
|
Id = API.LocalUser.Value.Id + 1,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -58,13 +73,164 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddUntilStep("placeholder shown", () => overlay.ChildrenOfType<BeatmapListingOverlay.NotFoundDrawable>().SingleOrDefault()?.IsPresent == true);
|
AddUntilStep("placeholder shown", () => overlay.ChildrenOfType<BeatmapListingOverlay.NotFoundDrawable>().SingleOrDefault()?.IsPresent == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUserWithoutSupporterUsesSupporterOnlyFiltersWithoutResults()
|
||||||
|
{
|
||||||
|
AddStep("fetch for 0 beatmaps", () => fetchFor());
|
||||||
|
AddStep("set dummy as non-supporter", () => ((DummyAPIAccess)API).LocalUser.Value.IsSupporter = false);
|
||||||
|
|
||||||
|
// only Rank Achieved filter
|
||||||
|
setRankAchievedFilter(new[] { ScoreRank.XH });
|
||||||
|
supporterRequiredPlaceholderShown();
|
||||||
|
|
||||||
|
setRankAchievedFilter(Array.Empty<ScoreRank>());
|
||||||
|
notFoundPlaceholderShown();
|
||||||
|
|
||||||
|
// only Played filter
|
||||||
|
setPlayedFilter(SearchPlayed.Played);
|
||||||
|
supporterRequiredPlaceholderShown();
|
||||||
|
|
||||||
|
setPlayedFilter(SearchPlayed.Any);
|
||||||
|
notFoundPlaceholderShown();
|
||||||
|
|
||||||
|
// both RankAchieved and Played filters
|
||||||
|
setRankAchievedFilter(new[] { ScoreRank.XH });
|
||||||
|
setPlayedFilter(SearchPlayed.Played);
|
||||||
|
supporterRequiredPlaceholderShown();
|
||||||
|
|
||||||
|
setRankAchievedFilter(Array.Empty<ScoreRank>());
|
||||||
|
setPlayedFilter(SearchPlayed.Any);
|
||||||
|
notFoundPlaceholderShown();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUserWithSupporterUsesSupporterOnlyFiltersWithoutResults()
|
||||||
|
{
|
||||||
|
AddStep("fetch for 0 beatmaps", () => fetchFor());
|
||||||
|
AddStep("set dummy as supporter", () => ((DummyAPIAccess)API).LocalUser.Value.IsSupporter = true);
|
||||||
|
|
||||||
|
// only Rank Achieved filter
|
||||||
|
setRankAchievedFilter(new[] { ScoreRank.XH });
|
||||||
|
notFoundPlaceholderShown();
|
||||||
|
|
||||||
|
setRankAchievedFilter(Array.Empty<ScoreRank>());
|
||||||
|
notFoundPlaceholderShown();
|
||||||
|
|
||||||
|
// only Played filter
|
||||||
|
setPlayedFilter(SearchPlayed.Played);
|
||||||
|
notFoundPlaceholderShown();
|
||||||
|
|
||||||
|
setPlayedFilter(SearchPlayed.Any);
|
||||||
|
notFoundPlaceholderShown();
|
||||||
|
|
||||||
|
// both Rank Achieved and Played filters
|
||||||
|
setRankAchievedFilter(new[] { ScoreRank.XH });
|
||||||
|
setPlayedFilter(SearchPlayed.Played);
|
||||||
|
notFoundPlaceholderShown();
|
||||||
|
|
||||||
|
setRankAchievedFilter(Array.Empty<ScoreRank>());
|
||||||
|
setPlayedFilter(SearchPlayed.Any);
|
||||||
|
notFoundPlaceholderShown();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUserWithoutSupporterUsesSupporterOnlyFiltersWithResults()
|
||||||
|
{
|
||||||
|
AddStep("fetch for 1 beatmap", () => fetchFor(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet));
|
||||||
|
AddStep("set dummy as non-supporter", () => ((DummyAPIAccess)API).LocalUser.Value.IsSupporter = false);
|
||||||
|
|
||||||
|
// only Rank Achieved filter
|
||||||
|
setRankAchievedFilter(new[] { ScoreRank.XH });
|
||||||
|
supporterRequiredPlaceholderShown();
|
||||||
|
|
||||||
|
setRankAchievedFilter(Array.Empty<ScoreRank>());
|
||||||
|
noPlaceholderShown();
|
||||||
|
|
||||||
|
// only Played filter
|
||||||
|
setPlayedFilter(SearchPlayed.Played);
|
||||||
|
supporterRequiredPlaceholderShown();
|
||||||
|
|
||||||
|
setPlayedFilter(SearchPlayed.Any);
|
||||||
|
noPlaceholderShown();
|
||||||
|
|
||||||
|
// both Rank Achieved and Played filters
|
||||||
|
setRankAchievedFilter(new[] { ScoreRank.XH });
|
||||||
|
setPlayedFilter(SearchPlayed.Played);
|
||||||
|
supporterRequiredPlaceholderShown();
|
||||||
|
|
||||||
|
setRankAchievedFilter(Array.Empty<ScoreRank>());
|
||||||
|
setPlayedFilter(SearchPlayed.Any);
|
||||||
|
noPlaceholderShown();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUserWithSupporterUsesSupporterOnlyFiltersWithResults()
|
||||||
|
{
|
||||||
|
AddStep("fetch for 1 beatmap", () => fetchFor(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet));
|
||||||
|
AddStep("set dummy as supporter", () => ((DummyAPIAccess)API).LocalUser.Value.IsSupporter = true);
|
||||||
|
|
||||||
|
// only Rank Achieved filter
|
||||||
|
setRankAchievedFilter(new[] { ScoreRank.XH });
|
||||||
|
noPlaceholderShown();
|
||||||
|
|
||||||
|
setRankAchievedFilter(Array.Empty<ScoreRank>());
|
||||||
|
noPlaceholderShown();
|
||||||
|
|
||||||
|
// only Played filter
|
||||||
|
setPlayedFilter(SearchPlayed.Played);
|
||||||
|
noPlaceholderShown();
|
||||||
|
|
||||||
|
setPlayedFilter(SearchPlayed.Any);
|
||||||
|
noPlaceholderShown();
|
||||||
|
|
||||||
|
// both Rank Achieved and Played filters
|
||||||
|
setRankAchievedFilter(new[] { ScoreRank.XH });
|
||||||
|
setPlayedFilter(SearchPlayed.Played);
|
||||||
|
noPlaceholderShown();
|
||||||
|
|
||||||
|
setRankAchievedFilter(Array.Empty<ScoreRank>());
|
||||||
|
setPlayedFilter(SearchPlayed.Any);
|
||||||
|
noPlaceholderShown();
|
||||||
|
}
|
||||||
|
|
||||||
private void fetchFor(params BeatmapSetInfo[] beatmaps)
|
private void fetchFor(params BeatmapSetInfo[] beatmaps)
|
||||||
{
|
{
|
||||||
setsForResponse.Clear();
|
setsForResponse.Clear();
|
||||||
setsForResponse.AddRange(beatmaps.Select(b => new TestAPIBeatmapSet(b)));
|
setsForResponse.AddRange(beatmaps.Select(b => new TestAPIBeatmapSet(b)));
|
||||||
|
|
||||||
// trigger arbitrary change for fetching.
|
// trigger arbitrary change for fetching.
|
||||||
overlay.ChildrenOfType<BeatmapListingSearchControl>().Single().Query.TriggerChange();
|
searchControl.Query.TriggerChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRankAchievedFilter(ScoreRank[] ranks)
|
||||||
|
{
|
||||||
|
AddStep($"set Rank Achieved filter to [{string.Join(',', ranks)}]", () =>
|
||||||
|
{
|
||||||
|
searchControl.Ranks.Clear();
|
||||||
|
searchControl.Ranks.AddRange(ranks);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPlayedFilter(SearchPlayed played)
|
||||||
|
{
|
||||||
|
AddStep($"set Played filter to {played}", () => searchControl.Played.Value = played);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void supporterRequiredPlaceholderShown()
|
||||||
|
{
|
||||||
|
AddUntilStep("\"supporter required\" placeholder shown", () => overlay.ChildrenOfType<BeatmapListingOverlay.SupporterRequiredDrawable>().SingleOrDefault()?.IsPresent == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notFoundPlaceholderShown()
|
||||||
|
{
|
||||||
|
AddUntilStep("\"no maps found\" placeholder shown", () => overlay.ChildrenOfType<BeatmapListingOverlay.NotFoundDrawable>().SingleOrDefault()?.IsPresent == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void noPlaceholderShown()
|
||||||
|
{
|
||||||
|
AddUntilStep("no placeholder shown", () =>
|
||||||
|
!overlay.ChildrenOfType<BeatmapListingOverlay.SupporterRequiredDrawable>().Any()
|
||||||
|
&& !overlay.ChildrenOfType<BeatmapListingOverlay.NotFoundDrawable>().Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestAPIBeatmapSet : APIBeatmapSet
|
private class TestAPIBeatmapSet : APIBeatmapSet
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -59,7 +60,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
private class Icon : Container, IHasTooltip
|
private class Icon : Container, IHasTooltip
|
||||||
{
|
{
|
||||||
public string TooltipText { get; }
|
public LocalisableString TooltipText { get; }
|
||||||
|
|
||||||
public SpriteIcon SpriteIcon { get; }
|
public SpriteIcon SpriteIcon { get; }
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
protected IAPIProvider API { get; private set; }
|
protected IAPIProvider API { get; private set; }
|
||||||
|
|
||||||
private readonly Bindable<string> beatmapId = new Bindable<string>();
|
private readonly Bindable<int?> beatmapId = new Bindable<int?>();
|
||||||
|
|
||||||
private readonly Bindable<string> mods = new Bindable<string>();
|
private readonly Bindable<string> mods = new Bindable<string>();
|
||||||
|
|
||||||
@ -220,14 +220,12 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(RulesetStore rulesets)
|
private void load(RulesetStore rulesets)
|
||||||
{
|
{
|
||||||
beatmapId.Value = Model.ID.ToString();
|
beatmapId.Value = Model.ID;
|
||||||
beatmapId.BindValueChanged(idString =>
|
beatmapId.BindValueChanged(id =>
|
||||||
{
|
{
|
||||||
int.TryParse(idString.NewValue, out var parsed);
|
Model.ID = id.NewValue ?? 0;
|
||||||
|
|
||||||
Model.ID = parsed;
|
if (id.NewValue != id.OldValue)
|
||||||
|
|
||||||
if (idString.NewValue != idString.OldValue)
|
|
||||||
Model.BeatmapInfo = null;
|
Model.BeatmapInfo = null;
|
||||||
|
|
||||||
if (Model.BeatmapInfo != null)
|
if (Model.BeatmapInfo != null)
|
||||||
|
@ -147,7 +147,7 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
protected IAPIProvider API { get; private set; }
|
protected IAPIProvider API { get; private set; }
|
||||||
|
|
||||||
private readonly Bindable<string> beatmapId = new Bindable<string>();
|
private readonly Bindable<int?> beatmapId = new Bindable<int?>();
|
||||||
|
|
||||||
private readonly Bindable<string> score = new Bindable<string>();
|
private readonly Bindable<string> score = new Bindable<string>();
|
||||||
|
|
||||||
@ -228,16 +228,12 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(RulesetStore rulesets)
|
private void load(RulesetStore rulesets)
|
||||||
{
|
{
|
||||||
beatmapId.Value = Model.ID.ToString();
|
beatmapId.Value = Model.ID;
|
||||||
beatmapId.BindValueChanged(idString =>
|
beatmapId.BindValueChanged(id =>
|
||||||
{
|
{
|
||||||
int parsed;
|
Model.ID = id.NewValue ?? 0;
|
||||||
|
|
||||||
int.TryParse(idString.NewValue, out parsed);
|
if (id.NewValue != id.OldValue)
|
||||||
|
|
||||||
Model.ID = parsed;
|
|
||||||
|
|
||||||
if (idString.NewValue != idString.OldValue)
|
|
||||||
Model.BeatmapInfo = null;
|
Model.BeatmapInfo = null;
|
||||||
|
|
||||||
if (Model.BeatmapInfo != null)
|
if (Model.BeatmapInfo != null)
|
||||||
|
@ -214,7 +214,7 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private TournamentGameBase game { get; set; }
|
private TournamentGameBase game { get; set; }
|
||||||
|
|
||||||
private readonly Bindable<string> userId = new Bindable<string>();
|
private readonly Bindable<int?> userId = new Bindable<int?>();
|
||||||
|
|
||||||
private readonly Container drawableContainer;
|
private readonly Container drawableContainer;
|
||||||
|
|
||||||
@ -278,14 +278,12 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
userId.Value = user.Id.ToString();
|
userId.Value = user.Id;
|
||||||
userId.BindValueChanged(idString =>
|
userId.BindValueChanged(id =>
|
||||||
{
|
{
|
||||||
int.TryParse(idString.NewValue, out var parsed);
|
user.Id = id.NewValue ?? 0;
|
||||||
|
|
||||||
user.Id = parsed;
|
if (id.NewValue != id.OldValue)
|
||||||
|
|
||||||
if (idString.NewValue != idString.OldValue)
|
|
||||||
user.Username = string.Empty;
|
user.Username = string.Empty;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(user.Username))
|
if (!string.IsNullOrEmpty(user.Username))
|
||||||
|
@ -191,8 +191,6 @@ namespace osu.Game.Beatmaps
|
|||||||
{
|
{
|
||||||
var beatmapIds = beatmapSet.Beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList();
|
var beatmapIds = beatmapSet.Beatmaps.Where(b => b.OnlineBeatmapID.HasValue).Select(b => b.OnlineBeatmapID).ToList();
|
||||||
|
|
||||||
LogForModel(beatmapSet, $"Validating online IDs for {beatmapSet.Beatmaps.Count} beatmaps...");
|
|
||||||
|
|
||||||
// ensure all IDs are unique
|
// ensure all IDs are unique
|
||||||
if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1))
|
if (beatmapIds.GroupBy(b => b).Any(g => g.Count() > 1))
|
||||||
{
|
{
|
||||||
|
@ -48,7 +48,6 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken)
|
public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
LogForModel(beatmapSet, "Performing online lookups...");
|
|
||||||
return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray());
|
return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Configuration
|
|||||||
{
|
{
|
||||||
public LocalisableString Label { get; }
|
public LocalisableString Label { get; }
|
||||||
|
|
||||||
public string Description { get; }
|
public LocalisableString Description { get; }
|
||||||
|
|
||||||
public int? OrderPosition { get; }
|
public int? OrderPosition { get; }
|
||||||
|
|
||||||
|
@ -788,7 +788,7 @@ namespace osu.Game.Database
|
|||||||
/// <param name="model">The model to populate.</param>
|
/// <param name="model">The model to populate.</param>
|
||||||
/// <param name="archive">The archive to use as a reference for population. May be null.</param>
|
/// <param name="archive">The archive to use as a reference for population. May be null.</param>
|
||||||
/// <param name="cancellationToken">An optional cancellation token.</param>
|
/// <param name="cancellationToken">An optional cancellation token.</param>
|
||||||
protected virtual Task Populate(TModel model, [CanBeNull] ArchiveReader archive, CancellationToken cancellationToken = default) => Task.CompletedTask;
|
protected abstract Task Populate(TModel model, [CanBeNull] ArchiveReader archive, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Perform any final actions before the import to database executes.
|
/// Perform any final actions before the import to database executes.
|
||||||
|
@ -4,12 +4,13 @@
|
|||||||
using Markdig.Syntax.Inlines;
|
using Markdig.Syntax.Inlines;
|
||||||
using osu.Framework.Graphics.Containers.Markdown;
|
using osu.Framework.Graphics.Containers.Markdown;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Containers.Markdown
|
namespace osu.Game.Graphics.Containers.Markdown
|
||||||
{
|
{
|
||||||
public class OsuMarkdownImage : MarkdownImage, IHasTooltip
|
public class OsuMarkdownImage : MarkdownImage, IHasTooltip
|
||||||
{
|
{
|
||||||
public string TooltipText { get; }
|
public LocalisableString TooltipText { get; }
|
||||||
|
|
||||||
public OsuMarkdownImage(LinkInline linkInline)
|
public OsuMarkdownImage(LinkInline linkInline)
|
||||||
: base(linkInline.Url)
|
: base(linkInline.Url)
|
||||||
|
@ -5,6 +5,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Containers
|
namespace osu.Game.Graphics.Containers
|
||||||
@ -24,7 +25,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
this.sampleSet = sampleSet;
|
this.sampleSet = sampleSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual string TooltipText { get; set; }
|
public virtual LocalisableString TooltipText { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Cursor
|
namespace osu.Game.Graphics.Cursor
|
||||||
@ -32,7 +33,7 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
|
|
||||||
public override bool SetContent(object content)
|
public override bool SetContent(object content)
|
||||||
{
|
{
|
||||||
if (!(content is string contentString))
|
if (!(content is LocalisableString contentString))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (contentString == text.Text) return true;
|
if (contentString == text.Text) return true;
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -58,6 +59,6 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TooltipText => "view in browser";
|
public LocalisableString TooltipText => "view in browser";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
@ -105,7 +106,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
private class CapsWarning : SpriteIcon, IHasTooltip
|
private class CapsWarning : SpriteIcon, IHasTooltip
|
||||||
{
|
{
|
||||||
public string TooltipText => @"caps lock is active";
|
public LocalisableString TooltipText => "caps lock is active";
|
||||||
|
|
||||||
public CapsWarning()
|
public CapsWarning()
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,7 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
@ -34,7 +35,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
private readonly Box rightBox;
|
private readonly Box rightBox;
|
||||||
private readonly Container nubContainer;
|
private readonly Container nubContainer;
|
||||||
|
|
||||||
public virtual string TooltipText { get; private set; }
|
public virtual LocalisableString TooltipText { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to format the tooltip as a percentage or the actual value.
|
/// Whether to format the tooltip as a percentage or the actual value.
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
@ -295,7 +296,7 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
|
|
||||||
public override bool Contains(Vector2 screenSpacePos) => content.Contains(screenSpacePos);
|
public override bool Contains(Vector2 screenSpacePos) => content.Contains(screenSpacePos);
|
||||||
|
|
||||||
public string TooltipText { get; }
|
public LocalisableString TooltipText { get; }
|
||||||
|
|
||||||
public ScoreComponentLabel(LeaderboardScoreStatistic statistic)
|
public ScoreComponentLabel(LeaderboardScoreStatistic statistic)
|
||||||
{
|
{
|
||||||
@ -365,7 +366,7 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TooltipText { get; }
|
public LocalisableString TooltipText { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LeaderboardScoreStatistic
|
public class LeaderboardScoreStatistic
|
||||||
|
@ -10,11 +10,13 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -23,9 +25,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
public class BeatmapListingFilterControl : CompositeDrawable
|
public class BeatmapListingFilterControl : CompositeDrawable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when a search finishes. Contains only new items in the case of pagination.
|
/// Fired when a search finishes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<List<BeatmapSetInfo>> SearchFinished;
|
public Action<SearchResult> SearchFinished;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when search criteria change.
|
/// Fired when search criteria change.
|
||||||
@ -212,7 +214,25 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
lastResponse = response;
|
lastResponse = response;
|
||||||
getSetsRequest = null;
|
getSetsRequest = null;
|
||||||
|
|
||||||
SearchFinished?.Invoke(sets);
|
// check if a non-supporter used supporter-only filters
|
||||||
|
if (!api.LocalUser.Value.IsSupporter)
|
||||||
|
{
|
||||||
|
List<LocalisableString> filters = new List<LocalisableString>();
|
||||||
|
|
||||||
|
if (searchControl.Played.Value != SearchPlayed.Any)
|
||||||
|
filters.Add(BeatmapsStrings.ListingSearchFiltersPlayed);
|
||||||
|
|
||||||
|
if (searchControl.Ranks.Any())
|
||||||
|
filters.Add(BeatmapsStrings.ListingSearchFiltersRank);
|
||||||
|
|
||||||
|
if (filters.Any())
|
||||||
|
{
|
||||||
|
SearchFinished?.Invoke(SearchResult.SupporterOnlyFilters(filters));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchFinished?.Invoke(SearchResult.ResultsReturned(sets));
|
||||||
};
|
};
|
||||||
|
|
||||||
api.Queue(getSetsRequest);
|
api.Queue(getSetsRequest);
|
||||||
@ -237,5 +257,53 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
|
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates the type of result of a user-requested beatmap search.
|
||||||
|
/// </summary>
|
||||||
|
public enum SearchResultType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Actual results have been returned from API.
|
||||||
|
/// </summary>
|
||||||
|
ResultsReturned,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user is not a supporter, but used supporter-only search filters.
|
||||||
|
/// </summary>
|
||||||
|
SupporterOnlyFilters
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes the result of a user-requested beatmap search.
|
||||||
|
/// </summary>
|
||||||
|
public struct SearchResult
|
||||||
|
{
|
||||||
|
public SearchResultType Type { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the beatmap sets returned from API.
|
||||||
|
/// Valid for read if and only if <see cref="Type"/> is <see cref="SearchResultType.ResultsReturned"/>.
|
||||||
|
/// </summary>
|
||||||
|
public List<BeatmapSetInfo> Results { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the names of supporter-only filters requested by the user.
|
||||||
|
/// Valid for read if and only if <see cref="Type"/> is <see cref="SearchResultType.SupporterOnlyFilters"/>.
|
||||||
|
/// </summary>
|
||||||
|
public List<LocalisableString> SupporterOnlyFiltersUsed { get; private set; }
|
||||||
|
|
||||||
|
public static SearchResult ResultsReturned(List<BeatmapSetInfo> results) => new SearchResult
|
||||||
|
{
|
||||||
|
Type = SearchResultType.ResultsReturned,
|
||||||
|
Results = results
|
||||||
|
};
|
||||||
|
|
||||||
|
public static SearchResult SupporterOnlyFilters(List<LocalisableString> filters) => new SearchResult
|
||||||
|
{
|
||||||
|
Type = SearchResultType.SupporterOnlyFilters,
|
||||||
|
SupporterOnlyFiltersUsed = filters
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@ -15,7 +16,9 @@ using osu.Framework.Graphics.Textures;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Overlays.BeatmapListing;
|
using osu.Game.Overlays.BeatmapListing;
|
||||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||||
using osu.Game.Resources.Localisation.Web;
|
using osu.Game.Resources.Localisation.Web;
|
||||||
@ -33,6 +36,7 @@ namespace osu.Game.Overlays
|
|||||||
private Container panelTarget;
|
private Container panelTarget;
|
||||||
private FillFlowContainer<BeatmapPanel> foundContent;
|
private FillFlowContainer<BeatmapPanel> foundContent;
|
||||||
private NotFoundDrawable notFoundContent;
|
private NotFoundDrawable notFoundContent;
|
||||||
|
private SupporterRequiredDrawable supporterRequiredContent;
|
||||||
private BeatmapListingFilterControl filterControl;
|
private BeatmapListingFilterControl filterControl;
|
||||||
|
|
||||||
public BeatmapListingOverlay()
|
public BeatmapListingOverlay()
|
||||||
@ -76,6 +80,7 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
foundContent = new FillFlowContainer<BeatmapPanel>(),
|
foundContent = new FillFlowContainer<BeatmapPanel>(),
|
||||||
notFoundContent = new NotFoundDrawable(),
|
notFoundContent = new NotFoundDrawable(),
|
||||||
|
supporterRequiredContent = new SupporterRequiredDrawable(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -115,9 +120,16 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private Task panelLoadDelegate;
|
private Task panelLoadDelegate;
|
||||||
|
|
||||||
private void onSearchFinished(List<BeatmapSetInfo> beatmaps)
|
private void onSearchFinished(BeatmapListingFilterControl.SearchResult searchResult)
|
||||||
{
|
{
|
||||||
var newPanels = beatmaps.Select<BeatmapSetInfo, BeatmapPanel>(b => new GridBeatmapPanel(b)
|
if (searchResult.Type == BeatmapListingFilterControl.SearchResultType.SupporterOnlyFilters)
|
||||||
|
{
|
||||||
|
supporterRequiredContent.UpdateText(searchResult.SupporterOnlyFiltersUsed);
|
||||||
|
addContentToPlaceholder(supporterRequiredContent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newPanels = searchResult.Results.Select<BeatmapSetInfo, BeatmapPanel>(b => new GridBeatmapPanel(b)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
@ -128,7 +140,7 @@ namespace osu.Game.Overlays
|
|||||||
//No matches case
|
//No matches case
|
||||||
if (!newPanels.Any())
|
if (!newPanels.Any())
|
||||||
{
|
{
|
||||||
LoadComponentAsync(notFoundContent, addContentToPlaceholder, (cancellationToken = new CancellationTokenSource()).Token);
|
addContentToPlaceholder(notFoundContent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,9 +182,9 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
var transform = lastContent.FadeOut(100, Easing.OutQuint);
|
var transform = lastContent.FadeOut(100, Easing.OutQuint);
|
||||||
|
|
||||||
if (lastContent == notFoundContent)
|
if (lastContent == notFoundContent || lastContent == supporterRequiredContent)
|
||||||
{
|
{
|
||||||
// not found display may be used multiple times, so don't expire/dispose it.
|
// the placeholders may be used multiple times, so don't expire/dispose them.
|
||||||
transform.Schedule(() => panelTarget.Remove(lastContent));
|
transform.Schedule(() => panelTarget.Remove(lastContent));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -240,6 +252,67 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: localisation requires Text/LinkFlowContainer support for localising strings with links inside
|
||||||
|
// (https://github.com/ppy/osu-framework/issues/4530)
|
||||||
|
public class SupporterRequiredDrawable : CompositeDrawable
|
||||||
|
{
|
||||||
|
private LinkFlowContainer supporterRequiredText;
|
||||||
|
|
||||||
|
public SupporterRequiredDrawable()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Height = 225;
|
||||||
|
Alpha = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(TextureStore textures)
|
||||||
|
{
|
||||||
|
AddInternal(new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Sprite
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
FillMode = FillMode.Fit,
|
||||||
|
Texture = textures.Get(@"Online/supporter-required"),
|
||||||
|
},
|
||||||
|
supporterRequiredText = new LinkFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Margin = new MarginPadding { Bottom = 10 },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateText(List<LocalisableString> filters)
|
||||||
|
{
|
||||||
|
supporterRequiredText.Clear();
|
||||||
|
|
||||||
|
supporterRequiredText.AddText(
|
||||||
|
BeatmapsStrings.ListingSearchSupporterFilterQuoteDefault(string.Join(" and ", filters), "").ToString(),
|
||||||
|
t =>
|
||||||
|
{
|
||||||
|
t.Font = OsuFont.GetFont(size: 16);
|
||||||
|
t.Colour = Colour4.White;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
supporterRequiredText.AddLink(BeatmapsStrings.ListingSearchSupporterFilterQuoteLinkText.ToString(), @"/store/products/supporter-tag");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private const double time_between_fetches = 500;
|
private const double time_between_fetches = 500;
|
||||||
|
|
||||||
private double lastFetchDisplayedTime;
|
private double lastFetchDisplayedTime;
|
||||||
|
@ -95,7 +95,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
{
|
{
|
||||||
private readonly OsuSpriteText value;
|
private readonly OsuSpriteText value;
|
||||||
|
|
||||||
public string TooltipText { get; }
|
public LocalisableString TooltipText { get; }
|
||||||
|
|
||||||
public LocalisableString Value
|
public LocalisableString Value
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -28,7 +29,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
|
|||||||
|
|
||||||
private readonly IBindable<User> localUser = new Bindable<User>();
|
private readonly IBindable<User> localUser = new Bindable<User>();
|
||||||
|
|
||||||
public string TooltipText
|
public LocalisableString TooltipText
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -26,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
|
|||||||
|
|
||||||
private readonly bool noVideo;
|
private readonly bool noVideo;
|
||||||
|
|
||||||
public string TooltipText => button.Enabled.Value ? "download this beatmap" : "login to download";
|
public LocalisableString TooltipText => button.Enabled.Value ? "download this beatmap" : "login to download";
|
||||||
|
|
||||||
private readonly IBindable<User> localUser = new Bindable<User>();
|
private readonly IBindable<User> localUser = new Bindable<User>();
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ using System;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Overlays.Comments.Buttons;
|
using osu.Game.Overlays.Comments.Buttons;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Comments
|
namespace osu.Game.Overlays.Comments
|
||||||
@ -395,7 +396,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
|
|
||||||
private class ParentUsername : FillFlowContainer, IHasTooltip
|
private class ParentUsername : FillFlowContainer, IHasTooltip
|
||||||
{
|
{
|
||||||
public string TooltipText => getParentMessage();
|
public LocalisableString TooltipText => getParentMessage();
|
||||||
|
|
||||||
private readonly Comment parentComment;
|
private readonly Comment parentComment;
|
||||||
|
|
||||||
@ -427,7 +428,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
if (parentComment == null)
|
if (parentComment == null)
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
return parentComment.HasMessage ? parentComment.Message : parentComment.IsDeleted ? @"deleted" : string.Empty;
|
return parentComment.HasMessage ? parentComment.Message : parentComment.IsDeleted ? "deleted" : string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<Mod> SelectionChanged;
|
public Action<Mod> SelectionChanged;
|
||||||
|
|
||||||
public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty;
|
public LocalisableString TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty;
|
||||||
|
|
||||||
private const Easing mod_switch_easing = Easing.InOutSine;
|
private const Easing mod_switch_easing = Easing.InOutSine;
|
||||||
private const double mod_switch_duration = 120;
|
private const double mod_switch_duration = 120;
|
||||||
|
@ -11,6 +11,7 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
@ -56,7 +57,7 @@ namespace osu.Game.Overlays
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OverlayColourProvider colourProvider { get; set; }
|
private OverlayColourProvider colourProvider { get; set; }
|
||||||
|
|
||||||
public string TooltipText => $@"{Value} view";
|
public LocalisableString TooltipText => $@"{Value} view";
|
||||||
|
|
||||||
private readonly SpriteIcon icon;
|
private readonly SpriteIcon icon;
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -42,6 +43,6 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
InternalChild.FadeInFromZero(200);
|
InternalChild.FadeInFromZero(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TooltipText => badge.Description;
|
public LocalisableString TooltipText => badge.Description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Profile.Header.Components
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
@ -14,7 +15,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
{
|
{
|
||||||
public readonly BindableBool DetailsVisible = new BindableBool();
|
public readonly BindableBool DetailsVisible = new BindableBool();
|
||||||
|
|
||||||
public override string TooltipText => DetailsVisible.Value ? "collapse" : "expand";
|
public override LocalisableString TooltipText => DetailsVisible.Value ? "collapse" : "expand";
|
||||||
|
|
||||||
private SpriteIcon icon;
|
private SpriteIcon icon;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Profile.Header.Components
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
@ -12,7 +13,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
{
|
{
|
||||||
public readonly Bindable<User> User = new Bindable<User>();
|
public readonly Bindable<User> User = new Bindable<User>();
|
||||||
|
|
||||||
public override string TooltipText => "followers";
|
public override LocalisableString TooltipText => "followers";
|
||||||
|
|
||||||
protected override IconUsage Icon => FontAwesome.Solid.User;
|
protected override IconUsage Icon => FontAwesome.Solid.User;
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
{
|
{
|
||||||
public readonly Bindable<User> User = new Bindable<User>();
|
public readonly Bindable<User> User = new Bindable<User>();
|
||||||
|
|
||||||
public string TooltipText { get; }
|
public LocalisableString TooltipText { get; }
|
||||||
|
|
||||||
private OsuSpriteText levelText;
|
private OsuSpriteText levelText;
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
{
|
{
|
||||||
public readonly Bindable<User> User = new Bindable<User>();
|
public readonly Bindable<User> User = new Bindable<User>();
|
||||||
|
|
||||||
public string TooltipText { get; }
|
public LocalisableString TooltipText { get; }
|
||||||
|
|
||||||
private Bar levelProgressBar;
|
private Bar levelProgressBar;
|
||||||
private OsuSpriteText levelProgressText;
|
private OsuSpriteText levelProgressText;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Profile.Header.Components
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
@ -12,7 +13,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
{
|
{
|
||||||
public readonly Bindable<User> User = new Bindable<User>();
|
public readonly Bindable<User> User = new Bindable<User>();
|
||||||
|
|
||||||
public override string TooltipText => "mapping subscribers";
|
public override LocalisableString TooltipText => "mapping subscribers";
|
||||||
|
|
||||||
protected override IconUsage Icon => FontAwesome.Solid.Bell;
|
protected override IconUsage Icon => FontAwesome.Solid.Bell;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.Chat;
|
using osu.Game.Online.Chat;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -16,7 +17,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
{
|
{
|
||||||
public readonly Bindable<User> User = new Bindable<User>();
|
public readonly Bindable<User> User = new Bindable<User>();
|
||||||
|
|
||||||
public override string TooltipText => "send message";
|
public override LocalisableString TooltipText => "send message";
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private ChannelManager channelManager { get; set; }
|
private ChannelManager channelManager { get; set; }
|
||||||
|
@ -6,6 +6,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Profile.Header.Components
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
@ -14,7 +15,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
{
|
{
|
||||||
public readonly Bindable<User> User = new Bindable<User>();
|
public readonly Bindable<User> User = new Bindable<User>();
|
||||||
|
|
||||||
public string TooltipText { get; set; }
|
public LocalisableString TooltipText { get; set; }
|
||||||
|
|
||||||
private OverlinedInfoContainer info;
|
private OverlinedInfoContainer info;
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Profile.Header.Components
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
private readonly FillFlowContainer iconContainer;
|
private readonly FillFlowContainer iconContainer;
|
||||||
private readonly CircularContainer content;
|
private readonly CircularContainer content;
|
||||||
|
|
||||||
public string TooltipText => "osu!supporter";
|
public LocalisableString TooltipText => "osu!supporter";
|
||||||
|
|
||||||
public int SupportLevel
|
public int SupportLevel
|
||||||
{
|
{
|
||||||
|
@ -143,7 +143,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
|
|||||||
|
|
||||||
private class PlayCountText : CompositeDrawable, IHasTooltip
|
private class PlayCountText : CompositeDrawable, IHasTooltip
|
||||||
{
|
{
|
||||||
public string TooltipText => "times played";
|
public LocalisableString TooltipText => "times played";
|
||||||
|
|
||||||
public PlayCountText(int playCount)
|
public PlayCountText(int playCount)
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Cursor;
|
|||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ namespace osu.Game.Overlays
|
|||||||
UpdateState();
|
UpdateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TooltipText => "revert to default";
|
public LocalisableString TooltipText => "revert to default";
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
|
49
osu.Game/Overlays/Settings/OutlinedTextBox.cs
Normal file
49
osu.Game/Overlays/Settings/OutlinedTextBox.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Settings
|
||||||
|
{
|
||||||
|
public class OutlinedTextBox : OsuTextBox
|
||||||
|
{
|
||||||
|
private const float border_thickness = 3;
|
||||||
|
|
||||||
|
private Color4 borderColourFocused;
|
||||||
|
private Color4 borderColourUnfocused;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colour)
|
||||||
|
{
|
||||||
|
borderColourUnfocused = colour.Gray4.Opacity(0.5f);
|
||||||
|
borderColourFocused = BorderColour;
|
||||||
|
|
||||||
|
updateBorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFocus(FocusEvent e)
|
||||||
|
{
|
||||||
|
base.OnFocus(e);
|
||||||
|
|
||||||
|
updateBorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFocusLost(FocusLostEvent e)
|
||||||
|
{
|
||||||
|
base.OnFocusLost(e);
|
||||||
|
|
||||||
|
updateBorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBorder()
|
||||||
|
{
|
||||||
|
BorderThickness = border_thickness;
|
||||||
|
BorderColour = HasFocus ? borderColourFocused : borderColourUnfocused;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
|||||||
|
|
||||||
private class OffsetSlider : OsuSliderBar<double>
|
private class OffsetSlider : OsuSliderBar<double>
|
||||||
{
|
{
|
||||||
public override string TooltipText => Current.Value.ToString(@"0ms");
|
public override LocalisableString TooltipText => Current.Value.ToString(@"0ms");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
|
|
||||||
private class UIScaleSlider : OsuSliderBar<float>
|
private class UIScaleSlider : OsuSliderBar<float>
|
||||||
{
|
{
|
||||||
public override string TooltipText => base.TooltipText + "x";
|
public override LocalisableString TooltipText => base.TooltipText + "x";
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ResolutionSettingsDropdown : SettingsDropdown<Size>
|
private class ResolutionSettingsDropdown : SettingsDropdown<Size>
|
||||||
|
@ -6,6 +6,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Handlers.Mouse;
|
using osu.Framework.Input.Handlers.Mouse;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input;
|
using osu.Game.Input;
|
||||||
@ -116,7 +117,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
|||||||
|
|
||||||
private class SensitivitySlider : OsuSliderBar<double>
|
private class SensitivitySlider : OsuSliderBar<double>
|
||||||
{
|
{
|
||||||
public override string TooltipText => Current.Disabled ? "enable high precision mouse to adjust sensitivity" : $"{base.TooltipText}x";
|
public override LocalisableString TooltipText => Current.Disabled ? "enable high precision mouse to adjust sensitivity" : $"{base.TooltipText}x";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings.Sections
|
namespace osu.Game.Overlays.Settings.Sections
|
||||||
@ -10,6 +11,6 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class SizeSlider : OsuSliderBar<float>
|
internal class SizeSlider : OsuSliderBar<float>
|
||||||
{
|
{
|
||||||
public override string TooltipText => Current.Value.ToString(@"0.##x");
|
public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
|||||||
|
|
||||||
private class TimeSlider : OsuSliderBar<float>
|
private class TimeSlider : OsuSliderBar<float>
|
||||||
{
|
{
|
||||||
public override string TooltipText => Current.Value.ToString("N0") + "ms";
|
public override LocalisableString TooltipText => Current.Value.ToString(@"N0") + "ms";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
@ -62,12 +63,12 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
|||||||
|
|
||||||
private class MaximumStarsSlider : StarsSlider
|
private class MaximumStarsSlider : StarsSlider
|
||||||
{
|
{
|
||||||
public override string TooltipText => Current.IsDefault ? "no limit" : base.TooltipText;
|
public override LocalisableString TooltipText => Current.IsDefault ? "no limit" : base.TooltipText;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class StarsSlider : OsuSliderBar<double>
|
private class StarsSlider : OsuSliderBar<double>
|
||||||
{
|
{
|
||||||
public override string TooltipText => Current.Value.ToString(@"0.## stars");
|
public override LocalisableString TooltipText => Current.Value.ToString(@"0.## stars");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
@ -17,14 +18,15 @@ namespace osu.Game.Overlays.Settings
|
|||||||
Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS };
|
Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS };
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TooltipText { get; set; }
|
public LocalisableString TooltipText { get; set; }
|
||||||
|
|
||||||
public override IEnumerable<string> FilterTerms
|
public override IEnumerable<string> FilterTerms
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (TooltipText != null)
|
if (TooltipText != default)
|
||||||
return base.FilterTerms.Append(TooltipText);
|
// TODO: this won't work as intended once the tooltip text is translated.
|
||||||
|
return base.FilterTerms.Append(TooltipText.ToString());
|
||||||
|
|
||||||
return base.FilterTerms;
|
return base.FilterTerms;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Settings
|
|||||||
|
|
||||||
public bool ShowsDefaultIndicator = true;
|
public bool ShowsDefaultIndicator = true;
|
||||||
|
|
||||||
public string TooltipText { get; set; }
|
public LocalisableString TooltipText { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuColour colours { get; set; }
|
private OsuColour colours { get; set; }
|
||||||
|
@ -1,19 +1,65 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
public class SettingsNumberBox : SettingsItem<string>
|
public class SettingsNumberBox : SettingsItem<int?>
|
||||||
{
|
{
|
||||||
protected override Drawable CreateControl() => new NumberBox
|
protected override Drawable CreateControl() => new NumberControl
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding { Top = 5 },
|
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Margin = new MarginPadding { Top = 5 }
|
||||||
};
|
};
|
||||||
|
|
||||||
public class NumberBox : SettingsTextBox.TextBox
|
private sealed class NumberControl : CompositeDrawable, IHasCurrentValue<int?>
|
||||||
|
{
|
||||||
|
private readonly BindableWithCurrent<int?> current = new BindableWithCurrent<int?>();
|
||||||
|
|
||||||
|
public Bindable<int?> Current
|
||||||
|
{
|
||||||
|
get => current.Current;
|
||||||
|
set => current.Current = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberControl()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
OutlinedNumberBox numberBox;
|
||||||
|
|
||||||
|
InternalChildren = new[]
|
||||||
|
{
|
||||||
|
numberBox = new OutlinedNumberBox
|
||||||
|
{
|
||||||
|
Margin = new MarginPadding { Top = 5 },
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
CommitOnFocusLost = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
numberBox.Current.BindValueChanged(e =>
|
||||||
|
{
|
||||||
|
int? value = null;
|
||||||
|
|
||||||
|
if (int.TryParse(e.NewValue, out var intVal))
|
||||||
|
value = intVal;
|
||||||
|
|
||||||
|
current.Value = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
Current.BindValueChanged(e =>
|
||||||
|
{
|
||||||
|
numberBox.Current.Value = e.NewValue?.ToString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OutlinedNumberBox : OutlinedTextBox
|
||||||
{
|
{
|
||||||
protected override bool CanAddCharacter(char character) => char.IsNumber(character);
|
protected override bool CanAddCharacter(char character) => char.IsNumber(character);
|
||||||
}
|
}
|
||||||
|
@ -1,60 +1,17 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
public class SettingsTextBox : SettingsItem<string>
|
public class SettingsTextBox : SettingsItem<string>
|
||||||
{
|
{
|
||||||
protected override Drawable CreateControl() => new TextBox
|
protected override Drawable CreateControl() => new OutlinedTextBox
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding { Top = 5 },
|
Margin = new MarginPadding { Top = 5 },
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
CommitOnFocusLost = true,
|
CommitOnFocusLost = true
|
||||||
};
|
};
|
||||||
|
|
||||||
public class TextBox : OsuTextBox
|
|
||||||
{
|
|
||||||
private const float border_thickness = 3;
|
|
||||||
|
|
||||||
private Color4 borderColourFocused;
|
|
||||||
private Color4 borderColourUnfocused;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colour)
|
|
||||||
{
|
|
||||||
borderColourUnfocused = colour.Gray4.Opacity(0.5f);
|
|
||||||
borderColourFocused = BorderColour;
|
|
||||||
|
|
||||||
updateBorder();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnFocus(FocusEvent e)
|
|
||||||
{
|
|
||||||
base.OnFocus(e);
|
|
||||||
|
|
||||||
updateBorder();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnFocusLost(FocusLostEvent e)
|
|
||||||
{
|
|
||||||
base.OnFocusLost(e);
|
|
||||||
|
|
||||||
updateBorder();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateBorder()
|
|
||||||
{
|
|
||||||
BorderThickness = border_thickness;
|
|
||||||
BorderColour = HasFocus ? borderColourFocused : borderColourUnfocused;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
// Audio
|
// Audio
|
||||||
new CheckAudioPresence(),
|
new CheckAudioPresence(),
|
||||||
new CheckAudioQuality(),
|
new CheckAudioQuality(),
|
||||||
|
new CheckMutedObjects(),
|
||||||
|
new CheckFewHitsounds(),
|
||||||
|
|
||||||
// Compose
|
// Compose
|
||||||
new CheckUnsnappedObjects(),
|
new CheckUnsnappedObjects(),
|
||||||
|
164
osu.Game/Rulesets/Edit/Checks/CheckFewHitsounds.cs
Normal file
164
osu.Game/Rulesets/Edit/Checks/CheckFewHitsounds.cs
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Rulesets.Edit.Checks.Components;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Edit.Checks
|
||||||
|
{
|
||||||
|
public class CheckFewHitsounds : ICheck
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 2 measures (4/4) of 120 BPM, typically makes up a few patterns in the map.
|
||||||
|
/// This is almost always ok, but can still be useful for the mapper to make sure hitsounding coverage is good.
|
||||||
|
/// </summary>
|
||||||
|
private const int negligible_threshold_time = 4000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 4 measures (4/4) of 120 BPM, typically makes up a large portion of a section in the song.
|
||||||
|
/// This is ok if the section is a quiet intro, for example.
|
||||||
|
/// </summary>
|
||||||
|
private const int warning_threshold_time = 8000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 12 measures (4/4) of 120 BPM, typically makes up multiple sections in the song.
|
||||||
|
/// </summary>
|
||||||
|
private const int problem_threshold_time = 24000;
|
||||||
|
|
||||||
|
// Should pass at least this many objects without hitsounds to be considered an issue (should work for Easy diffs too).
|
||||||
|
private const int warning_threshold_objects = 4;
|
||||||
|
private const int problem_threshold_objects = 16;
|
||||||
|
|
||||||
|
public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Audio, "Few or no hitsounds");
|
||||||
|
|
||||||
|
public IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
|
||||||
|
{
|
||||||
|
new IssueTemplateLongPeriodProblem(this),
|
||||||
|
new IssueTemplateLongPeriodWarning(this),
|
||||||
|
new IssueTemplateLongPeriodNegligible(this),
|
||||||
|
new IssueTemplateNoHitsounds(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
private bool mapHasHitsounds;
|
||||||
|
private int objectsWithoutHitsounds;
|
||||||
|
private double lastHitsoundTime;
|
||||||
|
|
||||||
|
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||||
|
{
|
||||||
|
if (!context.Beatmap.HitObjects.Any())
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
mapHasHitsounds = false;
|
||||||
|
objectsWithoutHitsounds = 0;
|
||||||
|
lastHitsoundTime = context.Beatmap.HitObjects.First().StartTime;
|
||||||
|
|
||||||
|
var hitObjectsIncludingNested = new List<HitObject>();
|
||||||
|
|
||||||
|
foreach (var hitObject in context.Beatmap.HitObjects)
|
||||||
|
{
|
||||||
|
// Samples play on the end of objects. Some objects have nested objects to accomplish playing them elsewhere (e.g. slider head/repeat).
|
||||||
|
foreach (var nestedHitObject in hitObject.NestedHitObjects)
|
||||||
|
hitObjectsIncludingNested.Add(nestedHitObject);
|
||||||
|
|
||||||
|
hitObjectsIncludingNested.Add(hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hitObjectsByEndTime = hitObjectsIncludingNested.OrderBy(o => o.GetEndTime()).ToList();
|
||||||
|
var hitObjectCount = hitObjectsByEndTime.Count;
|
||||||
|
|
||||||
|
for (int i = 0; i < hitObjectCount; ++i)
|
||||||
|
{
|
||||||
|
var hitObject = hitObjectsByEndTime[i];
|
||||||
|
|
||||||
|
// This is used to perform an update at the end so that the period after the last hitsounded object can be an issue.
|
||||||
|
bool isLastObject = i == hitObjectCount - 1;
|
||||||
|
|
||||||
|
foreach (var issue in applyHitsoundUpdate(hitObject, isLastObject))
|
||||||
|
yield return issue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mapHasHitsounds)
|
||||||
|
yield return new IssueTemplateNoHitsounds(this).Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Issue> applyHitsoundUpdate(HitObject hitObject, bool isLastObject = false)
|
||||||
|
{
|
||||||
|
var time = hitObject.GetEndTime();
|
||||||
|
bool hasHitsound = hitObject.Samples.Any(isHitsound);
|
||||||
|
bool couldHaveHitsound = hitObject.Samples.Any(isHitnormal);
|
||||||
|
|
||||||
|
// Only generating issues on hitsounded or last objects ensures we get one issue per long period.
|
||||||
|
// If there are no hitsounds we let the "No hitsounds" template take precedence.
|
||||||
|
if (hasHitsound || (isLastObject && mapHasHitsounds))
|
||||||
|
{
|
||||||
|
var timeWithoutHitsounds = time - lastHitsoundTime;
|
||||||
|
|
||||||
|
if (timeWithoutHitsounds > problem_threshold_time && objectsWithoutHitsounds > problem_threshold_objects)
|
||||||
|
yield return new IssueTemplateLongPeriodProblem(this).Create(lastHitsoundTime, timeWithoutHitsounds);
|
||||||
|
else if (timeWithoutHitsounds > warning_threshold_time && objectsWithoutHitsounds > warning_threshold_objects)
|
||||||
|
yield return new IssueTemplateLongPeriodWarning(this).Create(lastHitsoundTime, timeWithoutHitsounds);
|
||||||
|
else if (timeWithoutHitsounds > negligible_threshold_time && objectsWithoutHitsounds > warning_threshold_objects)
|
||||||
|
yield return new IssueTemplateLongPeriodNegligible(this).Create(lastHitsoundTime, timeWithoutHitsounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasHitsound)
|
||||||
|
{
|
||||||
|
mapHasHitsounds = true;
|
||||||
|
objectsWithoutHitsounds = 0;
|
||||||
|
lastHitsoundTime = time;
|
||||||
|
}
|
||||||
|
else if (couldHaveHitsound)
|
||||||
|
++objectsWithoutHitsounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isHitsound(HitSampleInfo sample) => HitSampleInfo.AllAdditions.Any(sample.Name.Contains);
|
||||||
|
private bool isHitnormal(HitSampleInfo sample) => sample.Name.Contains(HitSampleInfo.HIT_NORMAL);
|
||||||
|
|
||||||
|
public abstract class IssueTemplateLongPeriod : IssueTemplate
|
||||||
|
{
|
||||||
|
protected IssueTemplateLongPeriod(ICheck check, IssueType type)
|
||||||
|
: base(check, type, "Long period without hitsounds ({0:F1} seconds).")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Issue Create(double time, double duration) => new Issue(this, duration / 1000f) { Time = time };
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IssueTemplateLongPeriodProblem : IssueTemplateLongPeriod
|
||||||
|
{
|
||||||
|
public IssueTemplateLongPeriodProblem(ICheck check)
|
||||||
|
: base(check, IssueType.Problem)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IssueTemplateLongPeriodWarning : IssueTemplateLongPeriod
|
||||||
|
{
|
||||||
|
public IssueTemplateLongPeriodWarning(ICheck check)
|
||||||
|
: base(check, IssueType.Warning)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IssueTemplateLongPeriodNegligible : IssueTemplateLongPeriod
|
||||||
|
{
|
||||||
|
public IssueTemplateLongPeriodNegligible(ICheck check)
|
||||||
|
: base(check, IssueType.Negligible)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IssueTemplateNoHitsounds : IssueTemplate
|
||||||
|
{
|
||||||
|
public IssueTemplateNoHitsounds(ICheck check)
|
||||||
|
: base(check, IssueType.Problem, "There are no hitsounds.")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Issue Create() => new Issue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
158
osu.Game/Rulesets/Edit/Checks/CheckMutedObjects.cs
Normal file
158
osu.Game/Rulesets/Edit/Checks/CheckMutedObjects.cs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Rulesets.Edit.Checks.Components;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Edit.Checks
|
||||||
|
{
|
||||||
|
public class CheckMutedObjects : ICheck
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Volume percentages lower than or equal to this are typically inaudible.
|
||||||
|
/// </summary>
|
||||||
|
private const int muted_threshold = 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Volume percentages lower than or equal to this can sometimes be inaudible depending on sample used and music volume.
|
||||||
|
/// </summary>
|
||||||
|
private const int low_volume_threshold = 20;
|
||||||
|
|
||||||
|
private enum EdgeType
|
||||||
|
{
|
||||||
|
Head,
|
||||||
|
Repeat,
|
||||||
|
Tail,
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Audio, "Low volume hitobjects");
|
||||||
|
|
||||||
|
public IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
|
||||||
|
{
|
||||||
|
new IssueTemplateMutedActive(this),
|
||||||
|
new IssueTemplateLowVolumeActive(this),
|
||||||
|
new IssueTemplateMutedPassive(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||||
|
{
|
||||||
|
foreach (var hitObject in context.Beatmap.HitObjects)
|
||||||
|
{
|
||||||
|
// Worth keeping in mind: The samples of an object always play at its end time.
|
||||||
|
// Objects like spinners have no sound at its start because of this, while hold notes have nested objects to accomplish this.
|
||||||
|
foreach (var nestedHitObject in hitObject.NestedHitObjects)
|
||||||
|
{
|
||||||
|
foreach (var issue in getVolumeIssues(hitObject, nestedHitObject))
|
||||||
|
yield return issue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var issue in getVolumeIssues(hitObject))
|
||||||
|
yield return issue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Issue> getVolumeIssues(HitObject hitObject, HitObject sampledHitObject = null)
|
||||||
|
{
|
||||||
|
sampledHitObject ??= hitObject;
|
||||||
|
if (!sampledHitObject.Samples.Any())
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
// Samples that allow themselves to be overridden by control points have a volume of 0.
|
||||||
|
int maxVolume = sampledHitObject.Samples.Max(sample => sample.Volume > 0 ? sample.Volume : sampledHitObject.SampleControlPoint.SampleVolume);
|
||||||
|
double samplePlayTime = sampledHitObject.GetEndTime();
|
||||||
|
|
||||||
|
EdgeType edgeType = getEdgeAtTime(hitObject, samplePlayTime);
|
||||||
|
// We only care about samples played on the edges of objects, not ones like spinnerspin or slidertick.
|
||||||
|
if (edgeType == EdgeType.None)
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
string postfix = hitObject is IHasDuration ? edgeType.ToString().ToLower() : null;
|
||||||
|
|
||||||
|
if (maxVolume <= muted_threshold)
|
||||||
|
{
|
||||||
|
if (edgeType == EdgeType.Head)
|
||||||
|
yield return new IssueTemplateMutedActive(this).Create(hitObject, maxVolume / 100f, sampledHitObject.GetEndTime(), postfix);
|
||||||
|
else
|
||||||
|
yield return new IssueTemplateMutedPassive(this).Create(hitObject, maxVolume / 100f, sampledHitObject.GetEndTime(), postfix);
|
||||||
|
}
|
||||||
|
else if (maxVolume <= low_volume_threshold && edgeType == EdgeType.Head)
|
||||||
|
{
|
||||||
|
yield return new IssueTemplateLowVolumeActive(this).Create(hitObject, maxVolume / 100f, sampledHitObject.GetEndTime(), postfix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private EdgeType getEdgeAtTime(HitObject hitObject, double time)
|
||||||
|
{
|
||||||
|
if (Precision.AlmostEquals(time, hitObject.StartTime, 1f))
|
||||||
|
return EdgeType.Head;
|
||||||
|
if (Precision.AlmostEquals(time, hitObject.GetEndTime(), 1f))
|
||||||
|
return EdgeType.Tail;
|
||||||
|
|
||||||
|
if (hitObject is IHasRepeats hasRepeats)
|
||||||
|
{
|
||||||
|
double spanDuration = hasRepeats.Duration / hasRepeats.SpanCount();
|
||||||
|
if (spanDuration <= 0)
|
||||||
|
// Prevents undefined behaviour in cases like where zero/negative-length sliders/hold notes exist.
|
||||||
|
return EdgeType.None;
|
||||||
|
|
||||||
|
double spans = (time - hitObject.StartTime) / spanDuration;
|
||||||
|
double acceptableDifference = 1 / spanDuration; // 1 ms of acceptable difference, as with head/tail above.
|
||||||
|
|
||||||
|
if (Precision.AlmostEquals(spans, Math.Ceiling(spans), acceptableDifference) ||
|
||||||
|
Precision.AlmostEquals(spans, Math.Floor(spans), acceptableDifference))
|
||||||
|
{
|
||||||
|
return EdgeType.Repeat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EdgeType.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class IssueTemplateMuted : IssueTemplate
|
||||||
|
{
|
||||||
|
protected IssueTemplateMuted(ICheck check, IssueType type, string unformattedMessage)
|
||||||
|
: base(check, type, unformattedMessage)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Issue Create(HitObject hitobject, double volume, double time, string postfix = "")
|
||||||
|
{
|
||||||
|
string objectName = hitobject.GetType().Name;
|
||||||
|
if (!string.IsNullOrEmpty(postfix))
|
||||||
|
objectName += " " + postfix;
|
||||||
|
|
||||||
|
return new Issue(hitobject, this, objectName, volume) { Time = time };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IssueTemplateMutedActive : IssueTemplateMuted
|
||||||
|
{
|
||||||
|
public IssueTemplateMutedActive(ICheck check)
|
||||||
|
: base(check, IssueType.Problem, "{0} has a volume of {1:0%}. Clickable objects must have clearly audible feedback.")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IssueTemplateLowVolumeActive : IssueTemplateMuted
|
||||||
|
{
|
||||||
|
public IssueTemplateLowVolumeActive(ICheck check)
|
||||||
|
: base(check, IssueType.Warning, "{0} has a volume of {1:0%}, ensure this is audible.")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IssueTemplateMutedPassive : IssueTemplateMuted
|
||||||
|
{
|
||||||
|
public IssueTemplateMutedPassive(ICheck check)
|
||||||
|
: base(check, IssueType.Negligible, "{0} has a volume of {1:0%}, ensure there is no distinct sound here in the song if inaudible.")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
@ -16,7 +17,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override IconUsage? Icon => OsuIcon.Dice;
|
public override IconUsage? Icon => OsuIcon.Dice;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
[SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SeedSettingsControl))]
|
[SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))]
|
||||||
public Bindable<int?> Seed { get; } = new Bindable<int?>
|
public Bindable<int?> Seed { get; } = new Bindable<int?>
|
||||||
{
|
{
|
||||||
Default = null,
|
Default = null,
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.UserInterface;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Overlays.Settings;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A settings control for use by <see cref="IHasSeed"/> mods which have a customisable seed value.
|
|
||||||
/// </summary>
|
|
||||||
public class SeedSettingsControl : SettingsItem<int?>
|
|
||||||
{
|
|
||||||
protected override Drawable CreateControl() => new SeedControl
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Margin = new MarginPadding { Top = 5 }
|
|
||||||
};
|
|
||||||
|
|
||||||
private sealed class SeedControl : CompositeDrawable, IHasCurrentValue<int?>
|
|
||||||
{
|
|
||||||
private readonly BindableWithCurrent<int?> current = new BindableWithCurrent<int?>();
|
|
||||||
|
|
||||||
public Bindable<int?> Current
|
|
||||||
{
|
|
||||||
get => current;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
current.Current = value;
|
|
||||||
seedNumberBox.Text = value.Value.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly OsuNumberBox seedNumberBox;
|
|
||||||
|
|
||||||
public SeedControl()
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Y;
|
|
||||||
|
|
||||||
InternalChildren = new[]
|
|
||||||
{
|
|
||||||
new GridContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
ColumnDimensions = new[]
|
|
||||||
{
|
|
||||||
new Dimension(),
|
|
||||||
new Dimension(GridSizeMode.Absolute, 2),
|
|
||||||
new Dimension(GridSizeMode.Relative, 0.25f)
|
|
||||||
},
|
|
||||||
RowDimensions = new[]
|
|
||||||
{
|
|
||||||
new Dimension(GridSizeMode.AutoSize)
|
|
||||||
},
|
|
||||||
Content = new[]
|
|
||||||
{
|
|
||||||
new Drawable[]
|
|
||||||
{
|
|
||||||
seedNumberBox = new OsuNumberBox
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
CommitOnFocusLost = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
seedNumberBox.Current.BindValueChanged(e =>
|
|
||||||
{
|
|
||||||
int? value = null;
|
|
||||||
|
|
||||||
if (int.TryParse(e.NewValue, out var intVal))
|
|
||||||
value = intVal;
|
|
||||||
|
|
||||||
current.Value = value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
if (current.Value == null)
|
|
||||||
seedNumberBox.Text = current.Current.Value.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,6 +13,7 @@ using osu.Game.Graphics.Sprites;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.UI
|
namespace osu.Game.Rulesets.UI
|
||||||
{
|
{
|
||||||
@ -29,7 +30,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
private const float size = 80;
|
private const float size = 80;
|
||||||
|
|
||||||
public virtual string TooltipText => showTooltip ? mod.IconTooltip : null;
|
public virtual LocalisableString TooltipText => showTooltip ? mod.IconTooltip : null;
|
||||||
|
|
||||||
private Mod mod;
|
private Mod mod;
|
||||||
private readonly bool showTooltip;
|
private readonly bool showTooltip;
|
||||||
|
@ -7,6 +7,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -72,6 +73,9 @@ namespace osu.Game.Scoring
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Task Populate(ScoreInfo model, ArchiveReader archive, CancellationToken cancellationToken = default)
|
||||||
|
=> Task.CompletedTask;
|
||||||
|
|
||||||
protected override void ExportModelTo(ScoreInfo model, Stream outputStream)
|
protected override void ExportModelTo(ScoreInfo model, Stream outputStream)
|
||||||
{
|
{
|
||||||
var file = model.Files.SingleOrDefault();
|
var file = model.Files.SingleOrDefault();
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -58,6 +59,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
icon.FadeColour(!IsHeld && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint);
|
icon.FadeColour(!IsHeld && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TooltipText { get; }
|
public LocalisableString TooltipText { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
{
|
{
|
||||||
private readonly GameType type;
|
private readonly GameType type;
|
||||||
|
|
||||||
public string TooltipText => type.Name;
|
public LocalisableString TooltipText => type.Name;
|
||||||
|
|
||||||
public DrawableGameType(GameType type)
|
public DrawableGameType(GameType type)
|
||||||
{
|
{
|
||||||
|
@ -431,7 +431,7 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
public class InfoLabel : Container, IHasTooltip
|
public class InfoLabel : Container, IHasTooltip
|
||||||
{
|
{
|
||||||
public string TooltipText { get; }
|
public LocalisableString TooltipText { get; }
|
||||||
|
|
||||||
public InfoLabel(BeatmapStatistic statistic)
|
public InfoLabel(BeatmapStatistic statistic)
|
||||||
{
|
{
|
||||||
|
@ -42,27 +42,30 @@ namespace osu.Game.Skinning
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved]
|
private ISkinSource parentSource;
|
||||||
private ISkinSource skinSource { get; set; }
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
private void load()
|
|
||||||
{
|
{
|
||||||
UpdateSkins();
|
parentSource = parent.Get<ISkinSource>();
|
||||||
skinSource.SourceChanged += OnSourceChanged;
|
parentSource.SourceChanged += OnSourceChanged;
|
||||||
|
|
||||||
|
// ensure sources are populated and ready for use before childrens' asynchronous load flow.
|
||||||
|
UpdateSkinSources();
|
||||||
|
|
||||||
|
return base.CreateChildDependencies(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSourceChanged()
|
protected override void OnSourceChanged()
|
||||||
{
|
{
|
||||||
UpdateSkins();
|
UpdateSkinSources();
|
||||||
base.OnSourceChanged();
|
base.OnSourceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void UpdateSkins()
|
protected virtual void UpdateSkinSources()
|
||||||
{
|
{
|
||||||
SkinSources.Clear();
|
SkinSources.Clear();
|
||||||
|
|
||||||
foreach (var skin in skinSource.AllSources)
|
foreach (var skin in parentSource.AllSources)
|
||||||
{
|
{
|
||||||
switch (skin)
|
switch (skin)
|
||||||
{
|
{
|
||||||
@ -93,8 +96,8 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
if (skinSource != null)
|
if (parentSource != null)
|
||||||
skinSource.SourceChanged -= OnSourceChanged;
|
parentSource.SourceChanged -= OnSourceChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,16 +144,16 @@ namespace osu.Game.Skinning
|
|||||||
return base.ComputeHash(item, reader);
|
return base.ComputeHash(item, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task Populate(SkinInfo model, ArchiveReader archive, CancellationToken cancellationToken = default)
|
protected override Task Populate(SkinInfo model, ArchiveReader archive, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
await base.Populate(model, archive, cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var instance = GetSkin(model);
|
var instance = GetSkin(model);
|
||||||
|
|
||||||
model.InstantiationInfo ??= instance.GetType().GetInvariantInstantiationInfo();
|
model.InstantiationInfo ??= instance.GetType().GetInvariantInstantiationInfo();
|
||||||
|
|
||||||
if (model.Name?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true)
|
if (model.Name?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true)
|
||||||
populateMetadata(model, instance);
|
populateMetadata(model, instance);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateMetadata(SkinInfo item, Skin instance)
|
private void populateMetadata(SkinInfo item, Skin instance)
|
||||||
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
|
||||||
namespace osu.Game.Users.Drawables
|
namespace osu.Game.Users.Drawables
|
||||||
@ -68,11 +69,11 @@ namespace osu.Game.Users.Drawables
|
|||||||
|
|
||||||
private class ClickableArea : OsuClickableContainer
|
private class ClickableArea : OsuClickableContainer
|
||||||
{
|
{
|
||||||
private string tooltip = default_tooltip_text;
|
private LocalisableString tooltip = default_tooltip_text;
|
||||||
|
|
||||||
public override string TooltipText
|
public override LocalisableString TooltipText
|
||||||
{
|
{
|
||||||
get => Enabled.Value ? tooltip : null;
|
get => Enabled.Value ? tooltip : default;
|
||||||
set => tooltip = value;
|
set => tooltip = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Users.Drawables
|
namespace osu.Game.Users.Drawables
|
||||||
{
|
{
|
||||||
@ -13,7 +14,7 @@ namespace osu.Game.Users.Drawables
|
|||||||
{
|
{
|
||||||
private readonly Country country;
|
private readonly Country country;
|
||||||
|
|
||||||
public string TooltipText => country?.FullName;
|
public LocalisableString TooltipText => country?.FullName;
|
||||||
|
|
||||||
public DrawableFlag(Country country)
|
public DrawableFlag(Country country)
|
||||||
{
|
{
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.2.0" />
|
<PackageReference Include="Realm" Version="10.2.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.622.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.628.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.618.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.618.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.4.0" />
|
<PackageReference Include="Sentry" Version="3.4.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.622.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.628.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.618.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.618.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
@ -93,7 +93,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.622.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.628.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user