mirror of
https://github.com/ppy/osu.git
synced 2025-02-21 03:02:54 +08:00
Merge branch 'master' into gameplay/argon-key-counter_le-retour
This commit is contained in:
commit
d8b4db3a13
@ -11,7 +11,7 @@
|
|||||||
<AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger>
|
<AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.403.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.418.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" />
|
<AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" />
|
||||||
|
@ -48,7 +48,6 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
// todo: enable distance spacing once catch supports applying it to its existing distance snap grid implementation.
|
// todo: enable distance spacing once catch supports applying it to its existing distance snap grid implementation.
|
||||||
RightSideToolboxContainer.Alpha = 0;
|
|
||||||
DistanceSpacingMultiplier.Disabled = true;
|
DistanceSpacingMultiplier.Disabled = true;
|
||||||
|
|
||||||
LayerBelowRuleset.Add(new PlayfieldBorder
|
LayerBelowRuleset.Add(new PlayfieldBorder
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModDaycore : ModDaycore
|
public class CatchModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModDoubleTime : ModDoubleTime
|
public class CatchModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModHalfTime : ModHalfTime
|
public class CatchModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModNightcore : ModNightcore<CatchHitObject>
|
public class CatchModNightcore : ModNightcore<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModDaycore : ModDaycore
|
public class ManiaModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.5;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModDoubleTime : ModDoubleTime
|
public class ManiaModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModHalfTime : ModHalfTime
|
public class ManiaModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.5;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModNightcore : ModNightcore<ManiaHitObject>
|
public class ManiaModNightcore : ModNightcore<ManiaHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModDaycore : ModDaycore
|
public class OsuModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModDoubleTime : ModDoubleTime
|
public class OsuModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModHalfTime : ModHalfTime
|
public class OsuModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModNightcore : ModNightcore<OsuHitObject>
|
public class OsuModNightcore : ModNightcore<OsuHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModDaycore : ModDaycore
|
public class TaikoModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModDoubleTime : ModDoubleTime
|
public class TaikoModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModHalfTime : ModHalfTime
|
public class TaikoModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModNightcore : ModNightcore<TaikoHitObject>
|
public class TaikoModNightcore : ModNightcore<TaikoHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ namespace osu.Game.Tests.Rulesets
|
|||||||
this.parentManager = parentManager;
|
this.parentManager = parentManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override byte[] LoadRaw(string name) => parentManager.LoadRaw(name);
|
public override byte[] GetRawData(string fileName) => parentManager.GetRawData(fileName);
|
||||||
|
|
||||||
public bool IsDisposed { get; private set; }
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
|
@ -51,8 +51,8 @@ namespace osu.Game.Tests.Testing
|
|||||||
{
|
{
|
||||||
AddStep("ruleset shaders retrieved without error", () =>
|
AddStep("ruleset shaders retrieved without error", () =>
|
||||||
{
|
{
|
||||||
Dependencies.Get<ShaderManager>().LoadRaw(@"sh_TestVertex.vs");
|
Dependencies.Get<ShaderManager>().GetRawData(@"sh_TestVertex.vs");
|
||||||
Dependencies.Get<ShaderManager>().LoadRaw(@"sh_TestFragment.fs");
|
Dependencies.Get<ShaderManager>().GetRawData(@"sh_TestFragment.fs");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +164,36 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddAssert("all results reverted", () => playfield.JudgedObjects.Count, () => Is.EqualTo(0));
|
AddAssert("all results reverted", () => playfield.JudgedObjects.Count, () => Is.EqualTo(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRevertNestedObjects()
|
||||||
|
{
|
||||||
|
ManualClock clock = null;
|
||||||
|
|
||||||
|
var beatmap = new Beatmap();
|
||||||
|
beatmap.HitObjects.Add(new TestHitObjectWithNested { Duration = 40 });
|
||||||
|
|
||||||
|
createTest(beatmap, 10, () => new FramedClock(clock = new ManualClock()));
|
||||||
|
|
||||||
|
AddStep("skip to middle of object", () => clock.CurrentTime = (beatmap.HitObjects[0].StartTime + beatmap.HitObjects[0].GetEndTime()) / 2);
|
||||||
|
AddAssert("2 objects judged", () => playfield.JudgedObjects.Count, () => Is.EqualTo(2));
|
||||||
|
|
||||||
|
AddStep("skip to before end of object", () => clock.CurrentTime = beatmap.HitObjects[0].GetEndTime() - 1);
|
||||||
|
AddAssert("3 objects judged", () => playfield.JudgedObjects.Count, () => Is.EqualTo(3));
|
||||||
|
|
||||||
|
DrawableHitObject drawableHitObject = null;
|
||||||
|
HashSet<HitObject> revertedHitObjects = new HashSet<HitObject>();
|
||||||
|
|
||||||
|
AddStep("retrieve drawable hit object", () => drawableHitObject = playfield.ChildrenOfType<DrawableTestHitObjectWithNested>().Single());
|
||||||
|
AddStep("set up revert tracking", () =>
|
||||||
|
{
|
||||||
|
revertedHitObjects.Clear();
|
||||||
|
drawableHitObject.OnRevertResult += (ho, _) => revertedHitObjects.Add(ho.HitObject);
|
||||||
|
});
|
||||||
|
AddStep("skip back to object start", () => clock.CurrentTime = beatmap.HitObjects[0].StartTime);
|
||||||
|
AddAssert("3 reverts fired", () => revertedHitObjects, () => Has.Count.EqualTo(3));
|
||||||
|
AddAssert("no objects judged", () => playfield.JudgedObjects.Count, () => Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestApplyHitResultOnKilled()
|
public void TestApplyHitResultOnKilled()
|
||||||
{
|
{
|
||||||
@ -258,6 +288,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
RegisterPool<TestHitObject, DrawableTestHitObject>(poolSize);
|
RegisterPool<TestHitObject, DrawableTestHitObject>(poolSize);
|
||||||
RegisterPool<TestKilledHitObject, DrawableTestKilledHitObject>(poolSize);
|
RegisterPool<TestKilledHitObject, DrawableTestKilledHitObject>(poolSize);
|
||||||
|
RegisterPool<TestHitObjectWithNested, DrawableTestHitObjectWithNested>(poolSize);
|
||||||
|
RegisterPool<NestedHitObject, DrawableNestedHitObject>(poolSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new TestHitObjectLifetimeEntry(hitObject);
|
protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new TestHitObjectLifetimeEntry(hitObject);
|
||||||
@ -388,6 +420,120 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestHitObjectWithNested : TestHitObject
|
||||||
|
{
|
||||||
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
base.CreateNestedHitObjects(cancellationToken);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
AddNested(new NestedHitObject { StartTime = (float)Duration * (i + 1) / 4 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NestedHitObject : ConvertHitObject
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class DrawableTestHitObjectWithNested : DrawableHitObject<TestHitObjectWithNested>
|
||||||
|
{
|
||||||
|
private Container nestedContainer;
|
||||||
|
|
||||||
|
public DrawableTestHitObjectWithNested()
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AddRangeInternal(new Drawable[]
|
||||||
|
{
|
||||||
|
new Circle
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Colour4.Red
|
||||||
|
},
|
||||||
|
nestedContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnApply()
|
||||||
|
{
|
||||||
|
base.OnApply();
|
||||||
|
|
||||||
|
Size = new Vector2(200, 50);
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void AddNestedHitObject(DrawableHitObject hitObject)
|
||||||
|
{
|
||||||
|
base.AddNestedHitObject(hitObject);
|
||||||
|
nestedContainer.Add(hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ClearNestedHitObjects()
|
||||||
|
{
|
||||||
|
base.ClearNestedHitObjects();
|
||||||
|
nestedContainer.Clear(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
|
{
|
||||||
|
base.CheckForResult(userTriggered, timeOffset);
|
||||||
|
if (timeOffset >= 0)
|
||||||
|
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class DrawableNestedHitObject : DrawableHitObject<NestedHitObject>
|
||||||
|
{
|
||||||
|
public DrawableNestedHitObject()
|
||||||
|
: this(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawableNestedHitObject(NestedHitObject hitObject)
|
||||||
|
: base(hitObject)
|
||||||
|
{
|
||||||
|
Size = new Vector2(15);
|
||||||
|
Colour = Colour4.White;
|
||||||
|
RelativePositionAxes = Axes.Both;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AddInternal(new Circle
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnApply()
|
||||||
|
{
|
||||||
|
base.OnApply();
|
||||||
|
|
||||||
|
X = (float)((HitObject.StartTime - ParentHitObject!.HitObject.StartTime) / (ParentHitObject.HitObject.GetEndTime() - ParentHitObject.HitObject.StartTime));
|
||||||
|
Y = 0.5f;
|
||||||
|
|
||||||
|
LifetimeStart = ParentHitObject.LifetimeStart;
|
||||||
|
LifetimeEnd = ParentHitObject.LifetimeEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
|
{
|
||||||
|
base.CheckForResult(userTriggered, timeOffset);
|
||||||
|
if (timeOffset >= 0)
|
||||||
|
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps.Drawables;
|
using osu.Game.Beatmaps.Drawables;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
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.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
@ -26,7 +27,7 @@ using APIUser = osu.Game.Online.API.Requests.Responses.APIUser;
|
|||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
namespace osu.Game.Tests.Visual.Online
|
||||||
{
|
{
|
||||||
public partial class TestSceneBeatmapSetOverlay : OsuTestScene
|
public partial class TestSceneBeatmapSetOverlay : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
private readonly TestBeatmapSetOverlay overlay;
|
private readonly TestBeatmapSetOverlay overlay;
|
||||||
|
|
||||||
@ -281,6 +282,22 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddAssert(@"type is correct", () => type == lookupType.ToString());
|
AddAssert(@"type is correct", () => type == lookupType.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatmapSetWithGuestDifficulty()
|
||||||
|
{
|
||||||
|
AddStep("show map", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDifficulty()));
|
||||||
|
AddStep("move mouse to host difficulty", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(overlay.ChildrenOfType<DifficultyIcon>().ElementAt(0));
|
||||||
|
});
|
||||||
|
AddAssert("guest mapper information not shown", () => overlay.ChildrenOfType<BeatmapPicker>().Single().ChildrenOfType<OsuSpriteText>().All(s => s.Text != "BanchoBot"));
|
||||||
|
AddStep("move mouse to guest difficulty", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(overlay.ChildrenOfType<DifficultyIcon>().ElementAt(1));
|
||||||
|
});
|
||||||
|
AddAssert("guest mapper information shown", () => overlay.ChildrenOfType<BeatmapPicker>().Single().ChildrenOfType<OsuSpriteText>().Any(s => s.Text == "BanchoBot"));
|
||||||
|
}
|
||||||
|
|
||||||
private APIBeatmapSet createManyDifficultiesBeatmapSet()
|
private APIBeatmapSet createManyDifficultiesBeatmapSet()
|
||||||
{
|
{
|
||||||
var set = getBeatmapSet();
|
var set = getBeatmapSet();
|
||||||
@ -320,6 +337,60 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
return beatmapSet;
|
return beatmapSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private APIBeatmapSet createBeatmapSetWithGuestDifficulty()
|
||||||
|
{
|
||||||
|
var set = getBeatmapSet();
|
||||||
|
|
||||||
|
var beatmaps = new List<APIBeatmap>();
|
||||||
|
|
||||||
|
var guestUser = new APIUser
|
||||||
|
{
|
||||||
|
Username = @"BanchoBot",
|
||||||
|
Id = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
set.RelatedUsers = new[]
|
||||||
|
{
|
||||||
|
set.Author, guestUser
|
||||||
|
};
|
||||||
|
|
||||||
|
beatmaps.Add(new APIBeatmap
|
||||||
|
{
|
||||||
|
OnlineID = 1145,
|
||||||
|
DifficultyName = "Host Diff",
|
||||||
|
RulesetID = Ruleset.Value.OnlineID,
|
||||||
|
StarRating = 1.4,
|
||||||
|
OverallDifficulty = 3.5f,
|
||||||
|
AuthorID = set.AuthorID,
|
||||||
|
FailTimes = new APIFailTimes
|
||||||
|
{
|
||||||
|
Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(),
|
||||||
|
Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(),
|
||||||
|
},
|
||||||
|
Status = BeatmapOnlineStatus.Graveyard
|
||||||
|
});
|
||||||
|
|
||||||
|
beatmaps.Add(new APIBeatmap
|
||||||
|
{
|
||||||
|
OnlineID = 1919,
|
||||||
|
DifficultyName = "Guest Diff",
|
||||||
|
RulesetID = Ruleset.Value.OnlineID,
|
||||||
|
StarRating = 8.1,
|
||||||
|
OverallDifficulty = 3.5f,
|
||||||
|
AuthorID = 3,
|
||||||
|
FailTimes = new APIFailTimes
|
||||||
|
{
|
||||||
|
Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(),
|
||||||
|
Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(),
|
||||||
|
},
|
||||||
|
Status = BeatmapOnlineStatus.Graveyard
|
||||||
|
});
|
||||||
|
|
||||||
|
set.Beatmaps = beatmaps.ToArray();
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
private void downloadAssert(bool shown)
|
private void downloadAssert(bool shown)
|
||||||
{
|
{
|
||||||
AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.HeaderContent.DownloadButtonsVisible == shown);
|
AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.HeaderContent.DownloadButtonsVisible == shown);
|
||||||
|
@ -1068,6 +1068,21 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
AddAssert("options disabled", () => !songSelect.ChildrenOfType<FooterButtonOptions>().Single().Enabled.Value);
|
AddAssert("options disabled", () => !songSelect.ChildrenOfType<FooterButtonOptions>().Single().Enabled.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTextBoxBeatmapDifficultyCount()
|
||||||
|
{
|
||||||
|
createSongSelect();
|
||||||
|
|
||||||
|
AddAssert("0 matching shown", () => songSelect.ChildrenOfType<FilterControl>().Single().InformationalText == "0 matches");
|
||||||
|
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
|
||||||
|
AddAssert("3 matching shown", () => songSelect.ChildrenOfType<FilterControl>().Single().InformationalText == "3 matches");
|
||||||
|
AddStep("delete all beatmaps", () => manager.Delete());
|
||||||
|
AddUntilStep("wait for no beatmap", () => Beatmap.IsDefault);
|
||||||
|
AddAssert("0 matching shown", () => songSelect.ChildrenOfType<FilterControl>().Single().InformationalText == "0 matches");
|
||||||
|
}
|
||||||
|
|
||||||
private void waitForInitialSelection()
|
private void waitForInitialSelection()
|
||||||
{
|
{
|
||||||
AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
|
AddUntilStep("wait for initial selection", () => !Beatmap.IsDefault);
|
||||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
protected override string[] HashableFileTypes => new[] { ".osu" };
|
protected override string[] HashableFileTypes => new[] { ".osu" };
|
||||||
|
|
||||||
public Action<(BeatmapSetInfo beatmapSet, bool isBatch)>? ProcessBeatmap { private get; set; }
|
public ProcessBeatmapDelegate? ProcessBeatmap { private get; set; }
|
||||||
|
|
||||||
public BeatmapImporter(Storage storage, RealmAccess realm)
|
public BeatmapImporter(Storage storage, RealmAccess realm)
|
||||||
: base(storage, realm)
|
: base(storage, realm)
|
||||||
@ -59,7 +59,7 @@ namespace osu.Game.Beatmaps
|
|||||||
first.PerformRead(s =>
|
first.PerformRead(s =>
|
||||||
{
|
{
|
||||||
// Re-run processing even in this case. We might have outdated metadata.
|
// Re-run processing even in this case. We might have outdated metadata.
|
||||||
ProcessBeatmap?.Invoke((s, false));
|
ProcessBeatmap?.Invoke(s, MetadataLookupScope.OnlineFirst);
|
||||||
});
|
});
|
||||||
return first;
|
return first;
|
||||||
}
|
}
|
||||||
@ -206,7 +206,7 @@ namespace osu.Game.Beatmaps
|
|||||||
protected override void PostImport(BeatmapSetInfo model, Realm realm, ImportParameters parameters)
|
protected override void PostImport(BeatmapSetInfo model, Realm realm, ImportParameters parameters)
|
||||||
{
|
{
|
||||||
base.PostImport(model, realm, parameters);
|
base.PostImport(model, realm, parameters);
|
||||||
ProcessBeatmap?.Invoke((model, parameters.Batch));
|
ProcessBeatmap?.Invoke(model, parameters.Batch ? MetadataLookupScope.LocalCacheFirst : MetadataLookupScope.OnlineFirst);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateOnlineIds(BeatmapSetInfo beatmapSet, Realm realm)
|
private void validateOnlineIds(BeatmapSetInfo beatmapSet, Realm realm)
|
||||||
|
@ -167,7 +167,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public double DistanceSpacing { get; set; } = 1.0;
|
public double DistanceSpacing { get; set; } = 1.0;
|
||||||
|
|
||||||
public int BeatDivisor { get; set; }
|
public int BeatDivisor { get; set; } = 4;
|
||||||
|
|
||||||
public int GridSize { get; set; }
|
public int GridSize { get; set; }
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
private readonly WorkingBeatmapCache workingBeatmapCache;
|
private readonly WorkingBeatmapCache workingBeatmapCache;
|
||||||
|
|
||||||
public Action<(BeatmapSetInfo beatmapSet, bool isBatch)>? ProcessBeatmap { private get; set; }
|
public ProcessBeatmapDelegate? ProcessBeatmap { private get; set; }
|
||||||
|
|
||||||
public override bool PauseImports
|
public override bool PauseImports
|
||||||
{
|
{
|
||||||
@ -72,7 +72,7 @@ namespace osu.Game.Beatmaps
|
|||||||
BeatmapTrackStore = audioManager.GetTrackStore(userResources);
|
BeatmapTrackStore = audioManager.GetTrackStore(userResources);
|
||||||
|
|
||||||
beatmapImporter = CreateBeatmapImporter(storage, realm);
|
beatmapImporter = CreateBeatmapImporter(storage, realm);
|
||||||
beatmapImporter.ProcessBeatmap = args => ProcessBeatmap?.Invoke(args);
|
beatmapImporter.ProcessBeatmap = (beatmapSet, scope) => ProcessBeatmap?.Invoke(beatmapSet, scope);
|
||||||
beatmapImporter.PostNotification = obj => PostNotification?.Invoke(obj);
|
beatmapImporter.PostNotification = obj => PostNotification?.Invoke(obj);
|
||||||
|
|
||||||
workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host);
|
workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host);
|
||||||
@ -454,7 +454,9 @@ namespace osu.Game.Beatmaps
|
|||||||
if (transferCollections)
|
if (transferCollections)
|
||||||
beatmapInfo.TransferCollectionReferences(r, oldMd5Hash);
|
beatmapInfo.TransferCollectionReferences(r, oldMd5Hash);
|
||||||
|
|
||||||
ProcessBeatmap?.Invoke((liveBeatmapSet, false));
|
// do not look up metadata.
|
||||||
|
// this is a locally-modified set now, so looking up metadata is busy work at best and harmful at worst.
|
||||||
|
ProcessBeatmap?.Invoke(liveBeatmapSet, MetadataLookupScope.None);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,4 +544,11 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public override string HumanisedModelName => "beatmap";
|
public override string HumanisedModelName => "beatmap";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delegate type for beatmap processing callbacks.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmapSet">The beatmap set to be processed.</param>
|
||||||
|
/// <param name="lookupScope">The scope to use when looking up metadata.</param>
|
||||||
|
public delegate void ProcessBeatmapDelegate(BeatmapSetInfo beatmapSet, MetadataLookupScope lookupScope);
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Beatmaps
|
|||||||
var matchingSet = r.All<BeatmapSetInfo>().FirstOrDefault(s => s.OnlineID == id);
|
var matchingSet = r.All<BeatmapSetInfo>().FirstOrDefault(s => s.OnlineID == id);
|
||||||
|
|
||||||
if (matchingSet != null)
|
if (matchingSet != null)
|
||||||
beatmapUpdater.Queue(matchingSet.ToLive(realm), true);
|
beatmapUpdater.Queue(matchingSet.ToLive(realm), MetadataLookupScope.OnlineFirst);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -42,24 +42,25 @@ namespace osu.Game.Beatmaps
|
|||||||
/// Queue a beatmap for background processing.
|
/// Queue a beatmap for background processing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="beatmapSet">The managed beatmap set to update. A transaction will be opened to apply changes.</param>
|
/// <param name="beatmapSet">The managed beatmap set to update. A transaction will be opened to apply changes.</param>
|
||||||
/// <param name="preferOnlineFetch">Whether metadata from an online source should be preferred. If <c>true</c>, the local cache will be skipped to ensure the freshest data state possible.</param>
|
/// <param name="lookupScope">The preferred scope to use for metadata lookup.</param>
|
||||||
public void Queue(Live<BeatmapSetInfo> beatmapSet, bool preferOnlineFetch = false)
|
public void Queue(Live<BeatmapSetInfo> beatmapSet, MetadataLookupScope lookupScope = MetadataLookupScope.LocalCacheFirst)
|
||||||
{
|
{
|
||||||
Logger.Log($"Queueing change for local beatmap {beatmapSet}");
|
Logger.Log($"Queueing change for local beatmap {beatmapSet}");
|
||||||
Task.Factory.StartNew(() => beatmapSet.PerformRead(b => Process(b, preferOnlineFetch)), default, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
|
Task.Factory.StartNew(() => beatmapSet.PerformRead(b => Process(b, lookupScope)), default, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Run all processing on a beatmap immediately.
|
/// Run all processing on a beatmap immediately.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="beatmapSet">The managed beatmap set to update. A transaction will be opened to apply changes.</param>
|
/// <param name="beatmapSet">The managed beatmap set to update. A transaction will be opened to apply changes.</param>
|
||||||
/// <param name="preferOnlineFetch">Whether metadata from an online source should be preferred. If <c>true</c>, the local cache will be skipped to ensure the freshest data state possible.</param>
|
/// <param name="lookupScope">The preferred scope to use for metadata lookup.</param>
|
||||||
public void Process(BeatmapSetInfo beatmapSet, bool preferOnlineFetch = false) => beatmapSet.Realm.Write(r =>
|
public void Process(BeatmapSetInfo beatmapSet, MetadataLookupScope lookupScope = MetadataLookupScope.LocalCacheFirst) => beatmapSet.Realm.Write(r =>
|
||||||
{
|
{
|
||||||
// Before we use below, we want to invalidate.
|
// Before we use below, we want to invalidate.
|
||||||
workingBeatmapCache.Invalidate(beatmapSet);
|
workingBeatmapCache.Invalidate(beatmapSet);
|
||||||
|
|
||||||
metadataLookup.Update(beatmapSet, preferOnlineFetch);
|
if (lookupScope != MetadataLookupScope.None)
|
||||||
|
metadataLookup.Update(beatmapSet, lookupScope == MetadataLookupScope.OnlineFirst);
|
||||||
|
|
||||||
foreach (var beatmap in beatmapSet.Beatmaps)
|
foreach (var beatmap in beatmapSet.Beatmaps)
|
||||||
{
|
{
|
||||||
|
26
osu.Game/Beatmaps/MetadataLookupScope.cs
Normal file
26
osu.Game/Beatmaps/MetadataLookupScope.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines which sources (if any at all) should be queried in which order for a beatmap's metadata.
|
||||||
|
/// </summary>
|
||||||
|
public enum MetadataLookupScope
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Do not attempt to look up the beatmap metadata either in the local cache or online.
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try the local metadata cache first before querying online sources.
|
||||||
|
/// </summary>
|
||||||
|
LocalCacheFirst,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Query online sources immediately.
|
||||||
|
/// </summary>
|
||||||
|
OnlineFirst
|
||||||
|
}
|
||||||
|
}
|
@ -99,6 +99,16 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString TimelineTicks => new TranslatableString(getKey(@"timeline_ticks"), @"Ticks");
|
public static LocalisableString TimelineTicks => new TranslatableString(getKey(@"timeline_ticks"), @"Ticks");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "{0:0.0}°"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString RotationUnsnapped(float newRotation) => new TranslatableString(getKey(@"rotation_unsnapped"), @"{0:0.0}°", newRotation);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "{0:0.0}° (snapped)"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString RotationSnapped(float newRotation) => new TranslatableString(getKey(@"rotation_snapped"), @"{0:0.0}° (snapped)", newRotation);
|
||||||
|
|
||||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,9 @@ namespace osu.Game.Localisation
|
|||||||
public static LocalisableString Header => new TranslatableString(getKey(@"header"), @"Obtaining Beatmaps");
|
public static LocalisableString Header => new TranslatableString(getKey(@"header"), @"Obtaining Beatmaps");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ""Beatmaps" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection."
|
/// ""Beatmaps" are what we call sets of playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection."
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString Description => new TranslatableString(getKey(@"description"), @"""Beatmaps"" are what we call playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.");
|
public static LocalisableString Description => new TranslatableString(getKey(@"description"), @"""Beatmaps"" are what we call sets of playable levels. osu! doesn't come with any beatmaps pre-loaded. This step will help you get started on your beatmap collection.");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay."
|
/// "If you are a new player, we recommend playing through the tutorial to get accustomed to the gameplay."
|
||||||
|
@ -310,7 +310,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
base.Content.Add(new BeatmapOnlineChangeIngest(beatmapUpdater, realm, metadataClient));
|
base.Content.Add(new BeatmapOnlineChangeIngest(beatmapUpdater, realm, metadataClient));
|
||||||
|
|
||||||
BeatmapManager.ProcessBeatmap = args => beatmapUpdater.Process(args.beatmapSet, !args.isBatch);
|
BeatmapManager.ProcessBeatmap = (beatmapSet, scope) => beatmapUpdater.Process(beatmapSet, scope);
|
||||||
|
|
||||||
dependencies.Cache(userCache = new UserLookupCache());
|
dependencies.Cache(userCache = new UserLookupCache());
|
||||||
base.Content.Add(userCache);
|
base.Content.Add(userCache);
|
||||||
|
@ -107,7 +107,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Vertical = 20,
|
Vertical = 20,
|
||||||
Horizontal = 40,
|
Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING,
|
||||||
},
|
},
|
||||||
Child = new FillFlowContainer
|
Child = new FillFlowContainer
|
||||||
{
|
{
|
||||||
|
@ -31,6 +31,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
private const float tile_spacing = 2;
|
private const float tile_spacing = 2;
|
||||||
|
|
||||||
private readonly OsuSpriteText version, starRating, starRatingText;
|
private readonly OsuSpriteText version, starRating, starRatingText;
|
||||||
|
private readonly LinkFlowContainer guestMapperContainer;
|
||||||
private readonly FillFlowContainer starRatingContainer;
|
private readonly FillFlowContainer starRatingContainer;
|
||||||
private readonly Statistic plays, favourites;
|
private readonly Statistic plays, favourites;
|
||||||
|
|
||||||
@ -88,6 +89,14 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold)
|
Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold)
|
||||||
},
|
},
|
||||||
|
guestMapperContainer = new LinkFlowContainer(s =>
|
||||||
|
s.Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 11))
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Margin = new MarginPadding { Bottom = 1 },
|
||||||
|
},
|
||||||
starRatingContainer = new FillFlowContainer
|
starRatingContainer = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
@ -198,8 +207,21 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
updateDifficultyButtons();
|
updateDifficultyButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showBeatmap(IBeatmapInfo? beatmapInfo)
|
private void showBeatmap(APIBeatmap? beatmapInfo)
|
||||||
{
|
{
|
||||||
|
guestMapperContainer.Clear();
|
||||||
|
|
||||||
|
if (beatmapInfo?.AuthorID != BeatmapSet?.AuthorID)
|
||||||
|
{
|
||||||
|
APIUser? user = BeatmapSet?.RelatedUsers?.SingleOrDefault(u => u.OnlineID == beatmapInfo?.AuthorID);
|
||||||
|
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
guestMapperContainer.AddText("mapped by ");
|
||||||
|
guestMapperContainer.AddUserLink(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
version.Text = beatmapInfo?.DifficultyName ?? string.Empty;
|
version.Text = beatmapInfo?.DifficultyName ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +97,8 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Vertical = BeatmapSetOverlay.Y_PADDING,
|
Vertical = BeatmapSetOverlay.Y_PADDING,
|
||||||
Left = BeatmapSetOverlay.X_PADDING,
|
Left = WaveOverlayContainer.HORIZONTAL_PADDING,
|
||||||
Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH,
|
Right = WaveOverlayContainer.HORIZONTAL_PADDING + BeatmapSetOverlay.RIGHT_WIDTH,
|
||||||
},
|
},
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -170,7 +170,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = BeatmapSetOverlay.X_PADDING },
|
Margin = new MarginPadding { Top = BeatmapSetOverlay.Y_PADDING, Right = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(10),
|
Spacing = new Vector2(10),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Top = 15, Horizontal = BeatmapSetOverlay.X_PADDING },
|
Padding = new MarginPadding { Top = 15, Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
|
@ -116,7 +116,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Padding = new MarginPadding { Horizontal = 50 },
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Margin = new MarginPadding { Vertical = 20 },
|
Margin = new MarginPadding { Vertical = 20 },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,6 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
public partial class BeatmapSetOverlay : OnlineOverlay<BeatmapSetHeader>
|
public partial class BeatmapSetOverlay : OnlineOverlay<BeatmapSetHeader>
|
||||||
{
|
{
|
||||||
public const float X_PADDING = 40;
|
|
||||||
public const float Y_PADDING = 25;
|
public const float Y_PADDING = 25;
|
||||||
public const float RIGHT_WIDTH = 275;
|
public const float RIGHT_WIDTH = 275;
|
||||||
|
|
||||||
|
@ -18,8 +18,6 @@ namespace osu.Game.Overlays.Changelog
|
|||||||
{
|
{
|
||||||
public partial class ChangelogBuild : FillFlowContainer
|
public partial class ChangelogBuild : FillFlowContainer
|
||||||
{
|
{
|
||||||
public const float HORIZONTAL_PADDING = 70;
|
|
||||||
|
|
||||||
public Action<APIChangelogBuild> SelectBuild;
|
public Action<APIChangelogBuild> SelectBuild;
|
||||||
|
|
||||||
protected readonly APIChangelogBuild Build;
|
protected readonly APIChangelogBuild Build;
|
||||||
@ -33,7 +31,7 @@ namespace osu.Game.Overlays.Changelog
|
|||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
Direction = FillDirection.Vertical;
|
Direction = FillDirection.Vertical;
|
||||||
Padding = new MarginPadding { Horizontal = HORIZONTAL_PADDING };
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING };
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -93,7 +93,7 @@ namespace osu.Game.Overlays.Changelog
|
|||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Horizontal = 65,
|
Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING - ChangelogUpdateStreamItem.PADDING,
|
||||||
Vertical = 20
|
Vertical = 20
|
||||||
},
|
},
|
||||||
Child = Streams = new ChangelogUpdateStreamControl { Current = currentStream },
|
Child = Streams = new ChangelogUpdateStreamControl { Current = currentStream },
|
||||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Changelog
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = 1,
|
Height = 1,
|
||||||
Padding = new MarginPadding { Horizontal = ChangelogBuild.HORIZONTAL_PADDING },
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Margin = new MarginPadding { Top = 30 },
|
Margin = new MarginPadding { Top = 30 },
|
||||||
Child = new Box
|
Child = new Box
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Changelog
|
|||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Vertical = 20,
|
Vertical = 20,
|
||||||
Horizontal = 50,
|
Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Changelog
|
|||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Padding = new MarginPadding { Right = 50 + image_container_width },
|
Padding = new MarginPadding { Right = WaveOverlayContainer.HORIZONTAL_PADDING + image_container_width },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
|
@ -99,7 +99,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Horizontal = 50, Vertical = 20 },
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = 20 },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
avatar = new UpdateableAvatar(api.LocalUser.Value)
|
avatar = new UpdateableAvatar(api.LocalUser.Value)
|
||||||
@ -152,7 +152,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
ShowDeleted = { BindTarget = ShowDeleted },
|
ShowDeleted = { BindTarget = ShowDeleted },
|
||||||
Margin = new MarginPadding
|
Margin = new MarginPadding
|
||||||
{
|
{
|
||||||
Horizontal = 70,
|
Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING,
|
||||||
Vertical = 10
|
Vertical = 10
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -393,7 +393,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Margin = new MarginPadding { Left = 50 },
|
Margin = new MarginPadding { Left = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Text = CommentsStrings.Empty
|
Text = CommentsStrings.Empty
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Horizontal = 50 },
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new OverlaySortTabControl<CommentsSortCriteria>
|
new OverlaySortTabControl<CommentsSortCriteria>
|
||||||
|
@ -537,7 +537,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
{
|
{
|
||||||
return new MarginPadding
|
return new MarginPadding
|
||||||
{
|
{
|
||||||
Horizontal = 70,
|
Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING,
|
||||||
Vertical = 15
|
Vertical = 15
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Margin = new MarginPadding { Left = 50 },
|
Margin = new MarginPadding { Left = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Spacing = new Vector2(5, 0),
|
Spacing = new Vector2(5, 0),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -60,7 +60,7 @@ namespace osu.Game.Overlays.Dashboard
|
|||||||
new Container<BasicSearchTextBox>
|
new Container<BasicSearchTextBox>
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Padding = new MarginPadding(padding),
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = padding },
|
||||||
Child = searchTextBox = new BasicSearchTextBox
|
Child = searchTextBox = new BasicSearchTextBox
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
@ -79,7 +79,7 @@ namespace osu.Game.Overlays.Dashboard.Friends
|
|||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Top = 20,
|
Top = 20,
|
||||||
Horizontal = 45
|
Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING - FriendsOnlineStatusItem.PADDING
|
||||||
},
|
},
|
||||||
Child = onlineStreamControl = new FriendOnlineStreamControl(),
|
Child = onlineStreamControl = new FriendOnlineStreamControl(),
|
||||||
}
|
}
|
||||||
@ -129,7 +129,7 @@ namespace osu.Game.Overlays.Dashboard.Friends
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Horizontal = 50 }
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING }
|
||||||
},
|
},
|
||||||
loading = new LoadingLayer(true)
|
loading = new LoadingLayer(true)
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Overlays.News.Displays
|
|||||||
{
|
{
|
||||||
Vertical = 20,
|
Vertical = 20,
|
||||||
Left = 30,
|
Left = 30,
|
||||||
Right = 50
|
Right = WaveOverlayContainer.HORIZONTAL_PADDING
|
||||||
};
|
};
|
||||||
|
|
||||||
InternalChild = new FillFlowContainer
|
InternalChild = new FillFlowContainer
|
||||||
|
@ -89,7 +89,7 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ContentSidePadding = 50;
|
ContentSidePadding = WaveOverlayContainer.HORIZONTAL_PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Overlays
|
|||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Vertical = 20,
|
Vertical = 20,
|
||||||
Left = 50,
|
Left = WaveOverlayContainer.HORIZONTAL_PADDING,
|
||||||
Right = 30
|
Right = 30
|
||||||
},
|
},
|
||||||
Child = CreateContent()
|
Child = CreateContent()
|
||||||
|
@ -39,12 +39,14 @@ namespace osu.Game.Overlays
|
|||||||
private FillFlowContainer<SpriteText> text;
|
private FillFlowContainer<SpriteText> text;
|
||||||
private ExpandingBar expandingBar;
|
private ExpandingBar expandingBar;
|
||||||
|
|
||||||
|
public const float PADDING = 5;
|
||||||
|
|
||||||
protected OverlayStreamItem(T value)
|
protected OverlayStreamItem(T value)
|
||||||
: base(value)
|
: base(value)
|
||||||
{
|
{
|
||||||
Height = 50;
|
Height = 50;
|
||||||
Width = 90;
|
Width = 90;
|
||||||
Margin = new MarginPadding(5);
|
Margin = new MarginPadding(PADDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Spacing = new Vector2(10, 10),
|
Spacing = new Vector2(10, 10),
|
||||||
Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Top = 10 },
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Top = 10 },
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 },
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = 10 },
|
||||||
Spacing = new Vector2(0, 10),
|
Spacing = new Vector2(0, 10),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Padding = new MarginPadding { Vertical = 10 },
|
Padding = new MarginPadding { Vertical = 10 },
|
||||||
Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN },
|
Margin = new MarginPadding { Left = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Spacing = new Vector2(10, 0),
|
Spacing = new Vector2(10, 0),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Margin = new MarginPadding { Right = UserProfileOverlay.CONTENT_X_MARGIN },
|
Margin = new MarginPadding { Right = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
levelBadge = new LevelBadge
|
levelBadge = new LevelBadge
|
||||||
@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
Width = 200,
|
Width = 200,
|
||||||
Height = 6,
|
Height = 6,
|
||||||
Margin = new MarginPadding { Right = 50 },
|
Margin = new MarginPadding { Right = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Child = new LevelProgressBar
|
Child = new LevelProgressBar
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 },
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = 10 },
|
||||||
Child = new GridContainer
|
Child = new GridContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
@ -83,7 +83,7 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Left = UserProfileOverlay.CONTENT_X_MARGIN,
|
Left = WaveOverlayContainer.HORIZONTAL_PADDING,
|
||||||
Vertical = vertical_padding
|
Vertical = vertical_padding
|
||||||
},
|
},
|
||||||
Height = content_height + 2 * vertical_padding,
|
Height = content_height + 2 * vertical_padding,
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Profile
|
|||||||
|
|
||||||
public ProfileHeader()
|
public ProfileHeader()
|
||||||
{
|
{
|
||||||
ContentSidePadding = UserProfileOverlay.CONTENT_X_MARGIN;
|
ContentSidePadding = WaveOverlayContainer.HORIZONTAL_PADDING;
|
||||||
|
|
||||||
TabControl.AddItem(LayoutStrings.HeaderUsersShow);
|
TabControl.AddItem(LayoutStrings.HeaderUsersShow);
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Profile
|
|||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Margin = new MarginPadding
|
Margin = new MarginPadding
|
||||||
{
|
{
|
||||||
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN - outer_gutter_width,
|
Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING - outer_gutter_width,
|
||||||
Top = 20,
|
Top = 20,
|
||||||
Bottom = 20,
|
Bottom = 20,
|
||||||
},
|
},
|
||||||
@ -97,7 +97,7 @@ namespace osu.Game.Overlays.Profile
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN - outer_gutter_width,
|
Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING - outer_gutter_width,
|
||||||
Bottom = 20
|
Bottom = 20
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(10, 0),
|
Spacing = new Vector2(10, 0),
|
||||||
Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN },
|
Margin = new MarginPadding { Left = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN },
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING },
|
||||||
Child = new FillFlowContainer
|
Child = new FillFlowContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
@ -23,7 +23,6 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
public abstract partial class RankingsTable<TModel> : TableContainer
|
public abstract partial class RankingsTable<TModel> : TableContainer
|
||||||
{
|
{
|
||||||
protected const int TEXT_SIZE = 12;
|
protected const int TEXT_SIZE = 12;
|
||||||
private const float horizontal_inset = 20;
|
|
||||||
private const float row_height = 32;
|
private const float row_height = 32;
|
||||||
private const float row_spacing = 3;
|
private const float row_spacing = 3;
|
||||||
private const int items_per_page = 50;
|
private const int items_per_page = 50;
|
||||||
@ -39,7 +38,7 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
Padding = new MarginPadding { Horizontal = horizontal_inset };
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING };
|
||||||
RowSize = new Dimension(GridSizeMode.Absolute, row_height + row_spacing);
|
RowSize = new Dimension(GridSizeMode.Absolute, row_height + row_spacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +45,6 @@ namespace osu.Game.Overlays
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private RulesetStore rulesets { get; set; } = null!;
|
private RulesetStore rulesets { get; set; } = null!;
|
||||||
|
|
||||||
public const float CONTENT_X_MARGIN = 50;
|
|
||||||
|
|
||||||
public UserProfileOverlay()
|
public UserProfileOverlay()
|
||||||
: base(OverlayColourScheme.Pink)
|
: base(OverlayColourScheme.Pink)
|
||||||
{
|
{
|
||||||
@ -184,7 +182,7 @@ namespace osu.Game.Overlays
|
|||||||
public ProfileSectionTabControl()
|
public ProfileSectionTabControl()
|
||||||
{
|
{
|
||||||
Height = 40;
|
Height = 40;
|
||||||
Padding = new MarginPadding { Horizontal = CONTENT_X_MARGIN };
|
Padding = new MarginPadding { Horizontal = HORIZONTAL_PADDING };
|
||||||
TabContainer.Spacing = new Vector2(20);
|
TabContainer.Spacing = new Vector2(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
protected override string PopInSampleName => "UI/wave-pop-in";
|
protected override string PopInSampleName => "UI/wave-pop-in";
|
||||||
|
|
||||||
|
public const float HORIZONTAL_PADDING = 50;
|
||||||
|
|
||||||
protected WaveOverlayContainer()
|
protected WaveOverlayContainer()
|
||||||
{
|
{
|
||||||
AddInternal(Waves = new WaveContainer
|
AddInternal(Waves = new WaveContainer
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Wiki
|
|||||||
{
|
{
|
||||||
Vertical = 20,
|
Vertical = 20,
|
||||||
Left = 30,
|
Left = 30,
|
||||||
Right = 50,
|
Right = WaveOverlayContainer.HORIZONTAL_PADDING,
|
||||||
},
|
},
|
||||||
OnAddHeading = sidebar.AddEntry,
|
OnAddHeading = sidebar.AddEntry,
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ namespace osu.Game.Overlays
|
|||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Vertical = 20,
|
Vertical = 20,
|
||||||
Horizontal = 50,
|
Horizontal = HORIZONTAL_PADDING,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,6 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Extensions.LocalisationExtensions;
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
@ -47,8 +45,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
IBindable<double> IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier;
|
IBindable<double> IDistanceSnapProvider.DistanceSpacingMultiplier => DistanceSpacingMultiplier;
|
||||||
|
|
||||||
protected ExpandingToolboxContainer RightSideToolboxContainer { get; private set; }
|
|
||||||
|
|
||||||
private ExpandableSlider<double, SizeSlider<double>> distanceSpacingSlider;
|
private ExpandableSlider<double, SizeSlider<double>> distanceSpacingSlider;
|
||||||
private ExpandableButton currentDistanceSpacingButton;
|
private ExpandableButton currentDistanceSpacingButton;
|
||||||
|
|
||||||
@ -67,47 +63,29 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OverlayColourProvider colourProvider)
|
private void load(OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
AddInternal(new Container
|
RightToolbox.Add(new EditorToolboxGroup("snapping")
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1,
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
AutoSizeAxes = Axes.X,
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
distanceSpacingSlider = new ExpandableSlider<double, SizeSlider<double>>
|
||||||
{
|
{
|
||||||
Colour = colourProvider.Background5,
|
KeyboardStep = adjust_step,
|
||||||
RelativeSizeAxes = Axes.Both,
|
// Manual binding in LoadComplete to handle one-way event flow.
|
||||||
|
Current = DistanceSpacingMultiplier.GetUnboundCopy(),
|
||||||
},
|
},
|
||||||
RightSideToolboxContainer = new ExpandingToolboxContainer(130, 250)
|
currentDistanceSpacingButton = new ExpandableButton
|
||||||
{
|
{
|
||||||
Alpha = DistanceSpacingMultiplier.Disabled ? 0 : 1,
|
Action = () =>
|
||||||
Child = new EditorToolboxGroup("snapping")
|
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
(HitObject before, HitObject after)? objects = getObjectsOnEitherSideOfCurrentTime();
|
||||||
{
|
|
||||||
distanceSpacingSlider = new ExpandableSlider<double, SizeSlider<double>>
|
|
||||||
{
|
|
||||||
KeyboardStep = adjust_step,
|
|
||||||
// Manual binding in LoadComplete to handle one-way event flow.
|
|
||||||
Current = DistanceSpacingMultiplier.GetUnboundCopy(),
|
|
||||||
},
|
|
||||||
currentDistanceSpacingButton = new ExpandableButton
|
|
||||||
{
|
|
||||||
Action = () =>
|
|
||||||
{
|
|
||||||
(HitObject before, HitObject after)? objects = getObjectsOnEitherSideOfCurrentTime();
|
|
||||||
|
|
||||||
Debug.Assert(objects != null);
|
Debug.Assert(objects != null);
|
||||||
|
|
||||||
DistanceSpacingMultiplier.Value = ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after);
|
DistanceSpacingMultiplier.Value = ReadCurrentDistanceSnap(objects.Value.before, objects.Value.after);
|
||||||
DistanceSnapToggle.Value = TernaryState.True;
|
DistanceSnapToggle.Value = TernaryState.True;
|
||||||
},
|
},
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -261,7 +239,8 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
public virtual float GetBeatSnapDistanceAt(HitObject referenceObject, bool useReferenceSliderVelocity = true)
|
public virtual float GetBeatSnapDistanceAt(HitObject referenceObject, bool useReferenceSliderVelocity = true)
|
||||||
{
|
{
|
||||||
return (float)(100 * (useReferenceSliderVelocity ? referenceObject.DifficultyControlPoint.SliderVelocity : 1) * EditorBeatmap.Difficulty.SliderMultiplier * 1 / BeatSnapProvider.BeatDivisor);
|
return (float)(100 * (useReferenceSliderVelocity ? referenceObject.DifficultyControlPoint.SliderVelocity : 1) * EditorBeatmap.Difficulty.SliderMultiplier * 1
|
||||||
|
/ BeatSnapProvider.BeatDivisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual float DurationToDistance(HitObject referenceObject, double duration)
|
public virtual float DurationToDistance(HitObject referenceObject, double duration)
|
||||||
|
@ -58,8 +58,15 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
protected IBeatSnapProvider BeatSnapProvider { get; private set; }
|
protected IBeatSnapProvider BeatSnapProvider { get; private set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; }
|
||||||
|
|
||||||
protected ComposeBlueprintContainer BlueprintContainer { get; private set; }
|
protected ComposeBlueprintContainer BlueprintContainer { get; private set; }
|
||||||
|
|
||||||
|
protected ExpandingToolboxContainer LeftToolbox { get; private set; }
|
||||||
|
|
||||||
|
protected ExpandingToolboxContainer RightToolbox { get; private set; }
|
||||||
|
|
||||||
private DrawableEditorRulesetWrapper<TObject> drawableRulesetWrapper;
|
private DrawableEditorRulesetWrapper<TObject> drawableRulesetWrapper;
|
||||||
|
|
||||||
protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both };
|
protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
@ -82,7 +89,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OverlayColourProvider colourProvider, OsuConfigManager config)
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
autoSeekOnPlacement = config.GetBindable<bool>(OsuSetting.EditorAutoSeekOnPlacement);
|
autoSeekOnPlacement = config.GetBindable<bool>(OsuSetting.EditorAutoSeekOnPlacement);
|
||||||
|
|
||||||
@ -131,7 +138,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
Colour = colourProvider.Background5,
|
Colour = colourProvider.Background5,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
new ExpandingToolboxContainer(60, 200)
|
LeftToolbox = new ExpandingToolboxContainer(60, 200)
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -153,6 +160,28 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colourProvider.Background5,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
RightToolbox = new ExpandingToolboxContainer(130, 250)
|
||||||
|
{
|
||||||
|
Child = new EditorToolboxGroup("inspector")
|
||||||
|
{
|
||||||
|
Child = new HitObjectInspector()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
toolboxCollection.Items = CompositionTools
|
toolboxCollection.Items = CompositionTools
|
||||||
|
146
osu.Game/Rulesets/Edit/HitObjectInspector.cs
Normal file
146
osu.Game/Rulesets/Edit/HitObjectInspector.cs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.TypeExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Edit
|
||||||
|
{
|
||||||
|
internal partial class HitObjectInspector : CompositeDrawable
|
||||||
|
{
|
||||||
|
private OsuTextFlowContainer inspectorText = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
protected EditorBeatmap EditorBeatmap { get; private set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
InternalChild = inspectorText = new OsuTextFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
EditorBeatmap.SelectedHitObjects.CollectionChanged += (_, _) => updateInspectorText();
|
||||||
|
EditorBeatmap.TransactionBegan += updateInspectorText;
|
||||||
|
EditorBeatmap.TransactionEnded += updateInspectorText;
|
||||||
|
updateInspectorText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScheduledDelegate? rollingTextUpdate;
|
||||||
|
|
||||||
|
private void updateInspectorText()
|
||||||
|
{
|
||||||
|
inspectorText.Clear();
|
||||||
|
rollingTextUpdate?.Cancel();
|
||||||
|
rollingTextUpdate = null;
|
||||||
|
|
||||||
|
switch (EditorBeatmap.SelectedHitObjects.Count)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
addValue("No selection");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
var selected = EditorBeatmap.SelectedHitObjects.Single();
|
||||||
|
|
||||||
|
addHeader("Type");
|
||||||
|
addValue($"{selected.GetType().ReadableName()}");
|
||||||
|
|
||||||
|
addHeader("Time");
|
||||||
|
addValue($"{selected.StartTime:#,0.##}ms");
|
||||||
|
|
||||||
|
switch (selected)
|
||||||
|
{
|
||||||
|
case IHasPosition pos:
|
||||||
|
addHeader("Position");
|
||||||
|
addValue($"x:{pos.X:#,0.##} y:{pos.Y:#,0.##}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IHasXPosition x:
|
||||||
|
addHeader("Position");
|
||||||
|
|
||||||
|
addValue($"x:{x.X:#,0.##} ");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IHasYPosition y:
|
||||||
|
addHeader("Position");
|
||||||
|
|
||||||
|
addValue($"y:{y.Y:#,0.##}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected is IHasDistance distance)
|
||||||
|
{
|
||||||
|
addHeader("Distance");
|
||||||
|
addValue($"{distance.Distance:#,0.##}px");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected is IHasRepeats repeats)
|
||||||
|
{
|
||||||
|
addHeader("Repeats");
|
||||||
|
addValue($"{repeats.RepeatCount:#,0.##}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected is IHasDuration duration)
|
||||||
|
{
|
||||||
|
addHeader("End Time");
|
||||||
|
addValue($"{duration.EndTime:#,0.##}ms");
|
||||||
|
addHeader("Duration");
|
||||||
|
addValue($"{duration.Duration:#,0.##}ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
// I'd hope there's a better way to do this, but I don't want to bind to each and every property above to watch for changes.
|
||||||
|
// This is a good middle-ground for the time being.
|
||||||
|
rollingTextUpdate ??= Scheduler.AddDelayed(updateInspectorText, 250);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
addHeader("Selected Objects");
|
||||||
|
addValue($"{EditorBeatmap.SelectedHitObjects.Count:#,0.##}");
|
||||||
|
|
||||||
|
addHeader("Start Time");
|
||||||
|
addValue($"{EditorBeatmap.SelectedHitObjects.Min(o => o.StartTime):#,0.##}ms");
|
||||||
|
|
||||||
|
addHeader("End Time");
|
||||||
|
addValue($"{EditorBeatmap.SelectedHitObjects.Max(o => o.GetEndTime()):#,0.##}ms");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addHeader(string header) => inspectorText.AddParagraph($"{header}: ", s =>
|
||||||
|
{
|
||||||
|
s.Padding = new MarginPadding { Top = 2 };
|
||||||
|
s.Font = s.Font.With(size: 12);
|
||||||
|
s.Colour = colourProvider.Content2;
|
||||||
|
});
|
||||||
|
|
||||||
|
void addValue(string value) => inspectorText.AddParagraph(value, s =>
|
||||||
|
{
|
||||||
|
s.Font = s.Font.With(weight: FontWeight.SemiBold);
|
||||||
|
s.Colour = colourProvider.Content1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -24,5 +24,22 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
MaxValue = 2,
|
MaxValue = 2,
|
||||||
Precision = 0.01,
|
Precision = 0.01,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public override double ScoreMultiplier
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Round to the nearest multiple of 0.1.
|
||||||
|
double value = (int)(SpeedChange.Value * 10) / 10.0;
|
||||||
|
|
||||||
|
// Offset back to 0.
|
||||||
|
value -= 1;
|
||||||
|
|
||||||
|
// Each 0.1 multiple changes score multiplier by 0.02.
|
||||||
|
value /= 5;
|
||||||
|
|
||||||
|
return 1 + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,5 +24,19 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
MaxValue = 0.99,
|
MaxValue = 0.99,
|
||||||
Precision = 0.01,
|
Precision = 0.01,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public override double ScoreMultiplier
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Round to the nearest multiple of 0.1.
|
||||||
|
double value = (int)(SpeedChange.Value * 10) / 10.0;
|
||||||
|
|
||||||
|
// Offset back to 0.
|
||||||
|
value -= 1;
|
||||||
|
|
||||||
|
return 1 + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,6 +239,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
OnNestedDrawableCreated?.Invoke(drawableNested);
|
OnNestedDrawableCreated?.Invoke(drawableNested);
|
||||||
|
|
||||||
drawableNested.OnNewResult += onNewResult;
|
drawableNested.OnNewResult += onNewResult;
|
||||||
|
drawableNested.OnRevertResult += onNestedRevertResult;
|
||||||
drawableNested.ApplyCustomUpdateState += onApplyCustomUpdateState;
|
drawableNested.ApplyCustomUpdateState += onApplyCustomUpdateState;
|
||||||
|
|
||||||
// This is only necessary for non-pooled DHOs. For pooled DHOs, this is handled inside GetPooledDrawableRepresentation().
|
// This is only necessary for non-pooled DHOs. For pooled DHOs, this is handled inside GetPooledDrawableRepresentation().
|
||||||
@ -312,6 +313,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
foreach (var obj in nestedHitObjects)
|
foreach (var obj in nestedHitObjects)
|
||||||
{
|
{
|
||||||
obj.OnNewResult -= onNewResult;
|
obj.OnNewResult -= onNewResult;
|
||||||
|
obj.OnRevertResult -= onNestedRevertResult;
|
||||||
obj.ApplyCustomUpdateState -= onApplyCustomUpdateState;
|
obj.ApplyCustomUpdateState -= onApplyCustomUpdateState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,6 +378,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
OnRevertResult?.Invoke(this, Result);
|
OnRevertResult?.Invoke(this, Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onNestedRevertResult(DrawableHitObject drawableHitObject, JudgementResult result) => OnRevertResult?.Invoke(drawableHitObject, result);
|
||||||
|
|
||||||
private void onApplyCustomUpdateState(DrawableHitObject drawableHitObject, ArmedState state) => ApplyCustomUpdateState?.Invoke(drawableHitObject, state);
|
private void onApplyCustomUpdateState(DrawableHitObject drawableHitObject, ArmedState state) => ApplyCustomUpdateState?.Invoke(drawableHitObject, state);
|
||||||
|
|
||||||
private void onDefaultsApplied(HitObject hitObject)
|
private void onDefaultsApplied(HitObject hitObject)
|
||||||
|
@ -206,27 +206,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the debugger is attached, exceptions are expensive.
|
public override IShader? GetCachedShader(string vertex, string fragment) => base.GetCachedShader(vertex, fragment) ?? parent.GetCachedShader(vertex, fragment);
|
||||||
// Manually work around this by caching failed lookups and falling back straight to parent.
|
|
||||||
private readonly HashSet<(string, string)> failedLookups = new HashSet<(string, string)>();
|
|
||||||
|
|
||||||
public override IShader Load(string vertex, string fragment)
|
public override IShaderPart? GetCachedShaderPart(string name) => base.GetCachedShaderPart(name) ?? parent.GetCachedShaderPart(name);
|
||||||
{
|
|
||||||
if (!failedLookups.Contains((vertex, fragment)))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return base.Load(vertex, fragment);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Shader lookup is very non-standard. Rather than returning null on missing shaders, exceptions are thrown.
|
|
||||||
failedLookups.Add((vertex, fragment));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent.Load(vertex, fragment);
|
public override byte[]? GetRawData(string fileName) => base.GetRawData(fileName) ?? parent.GetRawData(fileName);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,15 @@ using System;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.EnumExtensions;
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
using osu.Framework.Extensions.LocalisationExtensions;
|
|
||||||
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.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Localisation;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
using Key = osuTK.Input.Key;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components
|
namespace osu.Game.Screens.Edit.Compose.Components
|
||||||
{
|
{
|
||||||
@ -26,6 +27,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
private SpriteIcon icon;
|
private SpriteIcon icon;
|
||||||
|
|
||||||
|
private const float snap_step = 15;
|
||||||
|
|
||||||
private readonly Bindable<float?> cumulativeRotation = new Bindable<float?>();
|
private readonly Bindable<float?> cumulativeRotation = new Bindable<float?>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
@ -50,18 +53,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
cumulativeRotation.BindValueChanged(_ => updateTooltipText(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateHoverState()
|
protected override void UpdateHoverState()
|
||||||
{
|
{
|
||||||
base.UpdateHoverState();
|
base.UpdateHoverState();
|
||||||
icon.FadeColour(!IsHeld && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint);
|
icon.FadeColour(!IsHeld && IsHovered ? Color4.White : Color4.Black, TRANSFORM_DURATION, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float rawCumulativeRotation;
|
||||||
|
|
||||||
protected override bool OnDragStart(DragStartEvent e)
|
protected override bool OnDragStart(DragStartEvent e)
|
||||||
{
|
{
|
||||||
bool handle = base.OnDragStart(e);
|
bool handle = base.OnDragStart(e);
|
||||||
@ -74,21 +73,36 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
base.OnDrag(e);
|
base.OnDrag(e);
|
||||||
|
|
||||||
float instantaneousAngle = convertDragEventToAngleOfRotation(e);
|
rawCumulativeRotation += convertDragEventToAngleOfRotation(e);
|
||||||
cumulativeRotation.Value += instantaneousAngle;
|
|
||||||
|
|
||||||
if (cumulativeRotation.Value < -180)
|
applyRotation(shouldSnap: e.ShiftPressed);
|
||||||
cumulativeRotation.Value += 360;
|
}
|
||||||
else if (cumulativeRotation.Value > 180)
|
|
||||||
cumulativeRotation.Value -= 360;
|
|
||||||
|
|
||||||
HandleRotate?.Invoke(instantaneousAngle);
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
|
{
|
||||||
|
if (IsDragged && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight))
|
||||||
|
{
|
||||||
|
applyRotation(shouldSnap: true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnKeyDown(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnKeyUp(KeyUpEvent e)
|
||||||
|
{
|
||||||
|
base.OnKeyUp(e);
|
||||||
|
|
||||||
|
if (IsDragged && (e.Key == Key.ShiftLeft || e.Key == Key.ShiftRight))
|
||||||
|
applyRotation(shouldSnap: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDragEnd(DragEndEvent e)
|
protected override void OnDragEnd(DragEndEvent e)
|
||||||
{
|
{
|
||||||
base.OnDragEnd(e);
|
base.OnDragEnd(e);
|
||||||
cumulativeRotation.Value = null;
|
cumulativeRotation.Value = null;
|
||||||
|
rawCumulativeRotation = 0;
|
||||||
|
TooltipText = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float convertDragEventToAngleOfRotation(DragEvent e)
|
private float convertDragEventToAngleOfRotation(DragEvent e)
|
||||||
@ -100,9 +114,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
return (endAngle - startAngle) * 180 / MathF.PI;
|
return (endAngle - startAngle) * 180 / MathF.PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTooltipText()
|
private void applyRotation(bool shouldSnap)
|
||||||
{
|
{
|
||||||
TooltipText = cumulativeRotation.Value?.ToLocalisableString("0.0°") ?? default;
|
float oldRotation = cumulativeRotation.Value ?? 0;
|
||||||
|
|
||||||
|
float newRotation = shouldSnap ? snap(rawCumulativeRotation, snap_step) : rawCumulativeRotation;
|
||||||
|
newRotation = (newRotation - 180) % 360 + 180;
|
||||||
|
|
||||||
|
cumulativeRotation.Value = newRotation;
|
||||||
|
|
||||||
|
HandleRotate?.Invoke(newRotation - oldRotation);
|
||||||
|
TooltipText = shouldSnap ? EditorStrings.RotationSnapped(newRotation) : EditorStrings.RotationUnsnapped(newRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float snap(float value, float step) => MathF.Round(value / step) * step;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Screens.Select
|
|||||||
public float BleedBottom { get; set; }
|
public float BleedBottom { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Triggered when the <see cref="BeatmapSets"/> loaded change and are completely loaded.
|
/// Triggered when <see cref="BeatmapSets"/> finish loading, or are subsequently changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action? BeatmapSetsChanged;
|
public Action? BeatmapSetsChanged;
|
||||||
|
|
||||||
@ -353,6 +353,8 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
if (!Scroll.UserScrolling)
|
if (!Scroll.UserScrolling)
|
||||||
ScrollToSelected(true);
|
ScrollToSelected(true);
|
||||||
|
|
||||||
|
BeatmapSetsChanged?.Invoke();
|
||||||
});
|
});
|
||||||
|
|
||||||
public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
|
public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Humanizer;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
@ -861,11 +862,9 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private void updateVisibleBeatmapCount()
|
private void updateVisibleBeatmapCount()
|
||||||
{
|
{
|
||||||
FilterControl.InformationalText = Carousel.CountDisplayed == 1
|
// Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918
|
||||||
// Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918
|
// but also in this case we want support for formatting a number within a string).
|
||||||
// but also in this case we want support for formatting a number within a string).
|
FilterControl.InformationalText = $"{"match".ToQuantity(Carousel.CountDisplayed, "#,0")}";
|
||||||
? $"{Carousel.CountDisplayed:#,0} matching beatmap"
|
|
||||||
: $"{Carousel.CountDisplayed:#,0} matching beatmaps";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool boundLocalBindables;
|
private bool boundLocalBindables;
|
||||||
|
@ -36,8 +36,8 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.20.0" />
|
<PackageReference Include="Realm" Version="10.20.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2023.403.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2023.418.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.402.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.417.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.28.1" />
|
<PackageReference Include="Sentry" Version="3.28.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
|
@ -16,6 +16,6 @@
|
|||||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.403.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.418.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
Loading…
Reference in New Issue
Block a user