mirror of
https://github.com/ppy/osu.git
synced 2025-01-22 12:32:55 +08:00
Merge branch 'master' into mutually-exclusive-save-related-actions
This commit is contained in:
commit
acf4dbb62c
@ -0,0 +1,96 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||||
|
{
|
||||||
|
public partial class TestSceneManiaSelectionHandler : EditorTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreateEditorRuleset() => new ManiaRuleset();
|
||||||
|
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHorizontalFlipOverSelection()
|
||||||
|
{
|
||||||
|
ManiaHitObject first = null!, second = null!, third = null!;
|
||||||
|
|
||||||
|
AddStep("create objects", () =>
|
||||||
|
{
|
||||||
|
EditorBeatmap.Add(first = new Note { StartTime = 250, Column = 2 });
|
||||||
|
EditorBeatmap.Add(second = new HoldNote { StartTime = 750, Duration = 1500, Column = 1 });
|
||||||
|
EditorBeatmap.Add(third = new Note { StartTime = 1250, Column = 3 });
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select everything", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
|
||||||
|
AddStep("flip horizontally over selection", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<SelectionBoxButton>().First());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("first object stayed in place", () => first.Column, () => Is.EqualTo(2));
|
||||||
|
AddAssert("second object flipped", () => second.Column, () => Is.EqualTo(3));
|
||||||
|
AddAssert("third object flipped", () => third.Column, () => Is.EqualTo(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHorizontalFlipOverPlayfield()
|
||||||
|
{
|
||||||
|
ManiaHitObject first = null!, second = null!, third = null!;
|
||||||
|
|
||||||
|
AddStep("create objects", () =>
|
||||||
|
{
|
||||||
|
EditorBeatmap.Add(first = new Note { StartTime = 250, Column = 2 });
|
||||||
|
EditorBeatmap.Add(second = new HoldNote { StartTime = 750, Duration = 1500, Column = 1 });
|
||||||
|
EditorBeatmap.Add(third = new Note { StartTime = 1250, Column = 3 });
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select everything", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
|
||||||
|
AddStep("flip horizontally", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.Key(Key.H);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("first object flipped", () => first.Column, () => Is.EqualTo(1));
|
||||||
|
AddAssert("second object flipped", () => second.Column, () => Is.EqualTo(2));
|
||||||
|
AddAssert("third object flipped", () => third.Column, () => Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestVerticalFlip()
|
||||||
|
{
|
||||||
|
ManiaHitObject first = null!, second = null!, third = null!;
|
||||||
|
|
||||||
|
AddStep("create objects", () =>
|
||||||
|
{
|
||||||
|
EditorBeatmap.Add(first = new Note { StartTime = 250, Column = 2 });
|
||||||
|
EditorBeatmap.Add(second = new HoldNote { StartTime = 750, Duration = 1500, Column = 1 });
|
||||||
|
EditorBeatmap.Add(third = new Note { StartTime = 1250, Column = 3 });
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select everything", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
|
||||||
|
AddStep("flip vertically", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.Key(Key.J);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("first object flipped", () => first.StartTime, () => Is.EqualTo(2250));
|
||||||
|
AddAssert("second object flipped", () => second.StartTime, () => Is.EqualTo(250));
|
||||||
|
AddAssert("third object flipped", () => third.StartTime, () => Is.EqualTo(1250));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -16,6 +17,16 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private HitObjectComposer composer { get; set; } = null!;
|
private HitObjectComposer composer { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override void OnSelectionChanged()
|
||||||
|
{
|
||||||
|
base.OnSelectionChanged();
|
||||||
|
|
||||||
|
var selectedObjects = SelectedItems.OfType<ManiaHitObject>().ToArray();
|
||||||
|
|
||||||
|
SelectionBox.CanFlipX = canFlipX(selectedObjects);
|
||||||
|
SelectionBox.CanFlipY = canFlipY(selectedObjects);
|
||||||
|
}
|
||||||
|
|
||||||
public override bool HandleMovement(MoveSelectionEvent<HitObject> moveEvent)
|
public override bool HandleMovement(MoveSelectionEvent<HitObject> moveEvent)
|
||||||
{
|
{
|
||||||
var hitObjectBlueprint = (HitObjectSelectionBlueprint)moveEvent.Blueprint;
|
var hitObjectBlueprint = (HitObjectSelectionBlueprint)moveEvent.Blueprint;
|
||||||
@ -26,6 +37,58 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool HandleFlip(Direction direction, bool flipOverOrigin)
|
||||||
|
{
|
||||||
|
var selectedObjects = SelectedItems.OfType<ManiaHitObject>().ToArray();
|
||||||
|
var maniaPlayfield = ((ManiaHitObjectComposer)composer).Playfield;
|
||||||
|
|
||||||
|
if (selectedObjects.Length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case Direction.Horizontal:
|
||||||
|
if (!canFlipX(selectedObjects))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int firstColumn = flipOverOrigin ? 0 : selectedObjects.Min(ho => ho.Column);
|
||||||
|
int lastColumn = flipOverOrigin ? (int)EditorBeatmap.BeatmapInfo.Difficulty.CircleSize - 1 : selectedObjects.Max(ho => ho.Column);
|
||||||
|
|
||||||
|
EditorBeatmap.PerformOnSelection(hitObject =>
|
||||||
|
{
|
||||||
|
var maniaObject = (ManiaHitObject)hitObject;
|
||||||
|
maniaPlayfield.Remove(maniaObject);
|
||||||
|
maniaObject.Column = firstColumn + (lastColumn - maniaObject.Column);
|
||||||
|
maniaPlayfield.Add(maniaObject);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Direction.Vertical:
|
||||||
|
if (!canFlipY(selectedObjects))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
double selectionStartTime = selectedObjects.Min(ho => ho.StartTime);
|
||||||
|
double selectionEndTime = selectedObjects.Max(ho => ho.GetEndTime());
|
||||||
|
|
||||||
|
EditorBeatmap.PerformOnSelection(hitObject =>
|
||||||
|
{
|
||||||
|
hitObject.StartTime = selectionStartTime + (selectionEndTime - hitObject.GetEndTime());
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(direction), direction, "Cannot flip over the supplied direction.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool canFlipX(ManiaHitObject[] selectedObjects)
|
||||||
|
=> selectedObjects.Select(ho => ho.Column).Distinct().Count() > 1;
|
||||||
|
|
||||||
|
private static bool canFlipY(ManiaHitObject[] selectedObjects)
|
||||||
|
=> selectedObjects.Length > 1 && selectedObjects.Min(ho => ho.StartTime) < selectedObjects.Max(ho => ho.GetEndTime());
|
||||||
|
|
||||||
private void performColumnMovement(int lastColumn, MoveSelectionEvent<HitObject> moveEvent)
|
private void performColumnMovement(int lastColumn, MoveSelectionEvent<HitObject> moveEvent)
|
||||||
{
|
{
|
||||||
var maniaPlayfield = ((ManiaHitObjectComposer)composer).Playfield;
|
var maniaPlayfield = ((ManiaHitObjectComposer)composer).Playfield;
|
||||||
|
BIN
osu.Game.Tests/Resources/Archives/modified-classic-20230809.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/modified-classic-20230809.osk
Normal file
Binary file not shown.
BIN
osu.Game.Tests/Resources/Archives/modified-default-20230809.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/modified-default-20230809.osk
Normal file
Binary file not shown.
@ -62,6 +62,10 @@ namespace osu.Game.Tests.Skins
|
|||||||
"Archives/modified-argon-20231108.osk",
|
"Archives/modified-argon-20231108.osk",
|
||||||
// Covers "Argon" performance points counter
|
// Covers "Argon" performance points counter
|
||||||
"Archives/modified-argon-20240305.osk",
|
"Archives/modified-argon-20240305.osk",
|
||||||
|
// Covers default rank display
|
||||||
|
"Archives/modified-default-20230809.osk",
|
||||||
|
// Covers legacy rank display
|
||||||
|
"Archives/modified-classic-20230809.osk"
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public partial class TestSceneSkinnableRankDisplay : SkinnableHUDComponentTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset());
|
||||||
|
|
||||||
|
private Bindable<ScoreRank> rank => (Bindable<ScoreRank>)scoreProcessor.Rank;
|
||||||
|
|
||||||
|
protected override Drawable CreateDefaultImplementation() => new DefaultRankDisplay();
|
||||||
|
|
||||||
|
protected override Drawable CreateLegacyImplementation() => new LegacyRankDisplay();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestChangingRank()
|
||||||
|
{
|
||||||
|
AddStep("Set rank to SS Hidden", () => rank.Value = ScoreRank.XH);
|
||||||
|
AddStep("Set rank to SS", () => rank.Value = ScoreRank.X);
|
||||||
|
AddStep("Set rank to S Hidden", () => rank.Value = ScoreRank.SH);
|
||||||
|
AddStep("Set rank to S", () => rank.Value = ScoreRank.S);
|
||||||
|
AddStep("Set rank to A", () => rank.Value = ScoreRank.A);
|
||||||
|
AddStep("Set rank to B", () => rank.Value = ScoreRank.B);
|
||||||
|
AddStep("Set rank to C", () => rank.Value = ScoreRank.C);
|
||||||
|
AddStep("Set rank to D", () => rank.Value = ScoreRank.D);
|
||||||
|
AddStep("Set rank to F", () => rank.Value = ScoreRank.F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
184
osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs
Normal file
184
osu.Game.Tests/Visual/SongSelect/TestSceneLeaderboardScoreV2.cs
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.SelectV2.Leaderboards;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.SongSelect
|
||||||
|
{
|
||||||
|
public partial class TestSceneLeaderboardScoreV2 : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuConfigManager config { get; set; } = null!;
|
||||||
|
|
||||||
|
private FillFlowContainer? fillFlow;
|
||||||
|
private OsuSpriteText? drawWidthText;
|
||||||
|
private float relativeWidth;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
// TODO: invalidation seems to be one-off when clicking slider to a certain value, so drag for now
|
||||||
|
// doesn't seem to happen in-game (when toggling window mode)
|
||||||
|
AddSliderStep("change relative width", 0, 1f, 0.6f, v =>
|
||||||
|
{
|
||||||
|
relativeWidth = v;
|
||||||
|
if (fillFlow != null) fillFlow.Width = v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
fillFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Width = relativeWidth,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Spacing = new Vector2(0f, 2f),
|
||||||
|
Shear = new Vector2(OsuGame.SHEAR, 0)
|
||||||
|
},
|
||||||
|
drawWidthText = new OsuSpriteText(),
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var scoreInfo in getTestScores())
|
||||||
|
{
|
||||||
|
fillFlow.Add(new LeaderboardScoreV2(scoreInfo, scoreInfo.Position, scoreInfo.User.Id == 2)
|
||||||
|
{
|
||||||
|
Shear = Vector2.Zero,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var score in fillFlow.Children)
|
||||||
|
score.Show();
|
||||||
|
});
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddToggleStep("toggle scoring mode", v => config.SetValue(OsuSetting.ScoreDisplayMode, v ? ScoringMode.Classic : ScoringMode.Standardised));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateAfterChildren()
|
||||||
|
{
|
||||||
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
|
if (drawWidthText != null) drawWidthText.Text = $"DrawWidth: {fillFlow?.DrawWidth}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ScoreInfo[] getTestScores()
|
||||||
|
{
|
||||||
|
var scores = new[]
|
||||||
|
{
|
||||||
|
new ScoreInfo
|
||||||
|
{
|
||||||
|
Position = 999,
|
||||||
|
Rank = ScoreRank.X,
|
||||||
|
Accuracy = 1,
|
||||||
|
MaxCombo = 244,
|
||||||
|
TotalScore = RNG.Next(1_800_000, 2_000_000),
|
||||||
|
MaximumStatistics = { { HitResult.Great, 3000 } },
|
||||||
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
User = new APIUser
|
||||||
|
{
|
||||||
|
Id = 6602580,
|
||||||
|
Username = @"waaiiru",
|
||||||
|
CountryCode = CountryCode.ES,
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
|
||||||
|
},
|
||||||
|
Date = DateTimeOffset.Now.AddYears(-2),
|
||||||
|
},
|
||||||
|
new ScoreInfo
|
||||||
|
{
|
||||||
|
Position = 22333,
|
||||||
|
Rank = ScoreRank.S,
|
||||||
|
Accuracy = 0.1f,
|
||||||
|
MaxCombo = 32040,
|
||||||
|
TotalScore = RNG.Next(1_200_000, 1_500_000),
|
||||||
|
MaximumStatistics = { { HitResult.Great, 3000 } },
|
||||||
|
Ruleset = new OsuRuleset().RulesetInfo,
|
||||||
|
User = new APIUser
|
||||||
|
{
|
||||||
|
Id = 1541390,
|
||||||
|
Username = @"Toukai",
|
||||||
|
CountryCode = CountryCode.CA,
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c2.jpg",
|
||||||
|
},
|
||||||
|
Date = DateTimeOffset.Now.AddMonths(-6),
|
||||||
|
},
|
||||||
|
TestResources.CreateTestScoreInfo(),
|
||||||
|
new ScoreInfo
|
||||||
|
{
|
||||||
|
Position = 110000,
|
||||||
|
Rank = ScoreRank.B,
|
||||||
|
Accuracy = 1,
|
||||||
|
MaxCombo = 244,
|
||||||
|
TotalScore = RNG.Next(1_000_000, 1_200_000),
|
||||||
|
MaximumStatistics = { { HitResult.Great, 3000 } },
|
||||||
|
Ruleset = new ManiaRuleset().RulesetInfo,
|
||||||
|
User = new APIUser
|
||||||
|
{
|
||||||
|
Username = @"No cover",
|
||||||
|
CountryCode = CountryCode.BR,
|
||||||
|
},
|
||||||
|
Date = DateTimeOffset.Now,
|
||||||
|
},
|
||||||
|
new ScoreInfo
|
||||||
|
{
|
||||||
|
Position = 110000,
|
||||||
|
Rank = ScoreRank.D,
|
||||||
|
Accuracy = 1,
|
||||||
|
MaxCombo = 244,
|
||||||
|
TotalScore = RNG.Next(500_000, 1_000_000),
|
||||||
|
MaximumStatistics = { { HitResult.Great, 3000 } },
|
||||||
|
Ruleset = new ManiaRuleset().RulesetInfo,
|
||||||
|
User = new APIUser
|
||||||
|
{
|
||||||
|
Id = 226597,
|
||||||
|
Username = @"WWWWWWWWWWWWWWWWWWWW",
|
||||||
|
CountryCode = CountryCode.US,
|
||||||
|
},
|
||||||
|
Date = DateTimeOffset.Now,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
scores[2].Rank = ScoreRank.A;
|
||||||
|
scores[2].TotalScore = RNG.Next(120_000, 400_000);
|
||||||
|
scores[2].MaximumStatistics[HitResult.Great] = 3000;
|
||||||
|
|
||||||
|
scores[1].Mods = new Mod[] { new OsuModHidden(), new OsuModDoubleTime(), new OsuModHardRock(), new OsuModFlashlight() };
|
||||||
|
scores[2].Mods = new Mod[] { new OsuModHidden(), new OsuModDoubleTime(), new OsuModHardRock(), new OsuModFlashlight(), new OsuModClassic() };
|
||||||
|
scores[3].Mods = new Mod[] { new OsuModHidden(), new OsuModDoubleTime(), new OsuModHardRock(), new OsuModFlashlight(), new OsuModClassic(), new OsuModDifficultyAdjust() };
|
||||||
|
scores[4].Mods = new ManiaRuleset().CreateAllMods().ToArray();
|
||||||
|
|
||||||
|
return scores;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
204
osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectV2.cs
Normal file
204
osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectV2.cs
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Screens;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
|
using osu.Game.Screens.Menu;
|
||||||
|
using osu.Game.Screens.SelectV2;
|
||||||
|
using osu.Game.Screens.SelectV2.Footer;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.SongSelect
|
||||||
|
{
|
||||||
|
public partial class TestSceneSongSelectV2 : ScreenTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private readonly ScreenFooter screenScreenFooter;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly OsuLogo logo;
|
||||||
|
|
||||||
|
public TestSceneSongSelectV2()
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new PopoverContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = screenScreenFooter = new ScreenFooter
|
||||||
|
{
|
||||||
|
OnBack = () => Stack.CurrentScreen.Exit(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
logo = new OsuLogo
|
||||||
|
{
|
||||||
|
Alpha = 0f,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Stack.ScreenPushed += updateFooter;
|
||||||
|
Stack.ScreenExited += updateFooter;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
base.SetUpSteps();
|
||||||
|
|
||||||
|
AddStep("load screen", () => Stack.Push(new SongSelectV2()));
|
||||||
|
AddUntilStep("wait for load", () => Stack.CurrentScreen is SongSelectV2 songSelect && songSelect.IsLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Footer
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMods()
|
||||||
|
{
|
||||||
|
AddStep("one mod", () => SelectedMods.Value = new List<Mod> { new OsuModHidden() });
|
||||||
|
AddStep("two mods", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock() });
|
||||||
|
AddStep("three mods", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime() });
|
||||||
|
AddStep("four mods", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic() });
|
||||||
|
AddStep("five mods", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic(), new OsuModDifficultyAdjust() });
|
||||||
|
|
||||||
|
AddStep("modified", () => SelectedMods.Value = new List<Mod> { new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } });
|
||||||
|
AddStep("modified + one", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } });
|
||||||
|
AddStep("modified + two", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } });
|
||||||
|
AddStep("modified + three", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModClassic(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } });
|
||||||
|
AddStep("modified + four", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModClassic(), new OsuModDifficultyAdjust(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } });
|
||||||
|
|
||||||
|
AddStep("clear mods", () => SelectedMods.Value = Array.Empty<Mod>());
|
||||||
|
AddWaitStep("wait", 3);
|
||||||
|
AddStep("one mod", () => SelectedMods.Value = new List<Mod> { new OsuModHidden() });
|
||||||
|
|
||||||
|
AddStep("clear mods", () => SelectedMods.Value = Array.Empty<Mod>());
|
||||||
|
AddWaitStep("wait", 3);
|
||||||
|
AddStep("five mods", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic(), new OsuModDifficultyAdjust() });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShowOptions()
|
||||||
|
{
|
||||||
|
AddStep("enable options", () =>
|
||||||
|
{
|
||||||
|
var optionsButton = this.ChildrenOfType<ScreenFooterButton>().Last();
|
||||||
|
|
||||||
|
optionsButton.Enabled.Value = true;
|
||||||
|
optionsButton.TriggerClick();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestState()
|
||||||
|
{
|
||||||
|
AddToggleStep("set options enabled state", state => this.ChildrenOfType<ScreenFooterButton>().Last().Enabled.Value = state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add these test cases when functionality is implemented.
|
||||||
|
// [Test]
|
||||||
|
// public void TestFooterRandom()
|
||||||
|
// {
|
||||||
|
// AddStep("press F2", () => InputManager.Key(Key.F2));
|
||||||
|
// AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// [Test]
|
||||||
|
// public void TestFooterRandomViaMouse()
|
||||||
|
// {
|
||||||
|
// AddStep("click button", () =>
|
||||||
|
// {
|
||||||
|
// InputManager.MoveMouseTo(randomButton);
|
||||||
|
// InputManager.Click(MouseButton.Left);
|
||||||
|
// });
|
||||||
|
// AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// [Test]
|
||||||
|
// public void TestFooterRewind()
|
||||||
|
// {
|
||||||
|
// AddStep("press Shift+F2", () =>
|
||||||
|
// {
|
||||||
|
// InputManager.PressKey(Key.LShift);
|
||||||
|
// InputManager.PressKey(Key.F2);
|
||||||
|
// InputManager.ReleaseKey(Key.F2);
|
||||||
|
// InputManager.ReleaseKey(Key.LShift);
|
||||||
|
// });
|
||||||
|
// AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// [Test]
|
||||||
|
// public void TestFooterRewindViaShiftMouseLeft()
|
||||||
|
// {
|
||||||
|
// AddStep("shift + click button", () =>
|
||||||
|
// {
|
||||||
|
// InputManager.PressKey(Key.LShift);
|
||||||
|
// InputManager.MoveMouseTo(randomButton);
|
||||||
|
// InputManager.Click(MouseButton.Left);
|
||||||
|
// InputManager.ReleaseKey(Key.LShift);
|
||||||
|
// });
|
||||||
|
// AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// [Test]
|
||||||
|
// public void TestFooterRewindViaMouseRight()
|
||||||
|
// {
|
||||||
|
// AddStep("right click button", () =>
|
||||||
|
// {
|
||||||
|
// InputManager.MoveMouseTo(randomButton);
|
||||||
|
// InputManager.Click(MouseButton.Right);
|
||||||
|
// });
|
||||||
|
// AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
||||||
|
// }
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOverlayPresent()
|
||||||
|
{
|
||||||
|
AddStep("Press F1", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(this.ChildrenOfType<ScreenFooterButtonMods>().Single());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("Overlay visible", () => this.ChildrenOfType<ModSelectOverlay>().Single().State.Value == Visibility.Visible);
|
||||||
|
AddStep("Hide", () => this.ChildrenOfType<ModSelectOverlay>().Single().Hide());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
Stack.Padding = new MarginPadding { Bottom = screenScreenFooter.DrawHeight - screenScreenFooter.Y };
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFooter(IScreen? _, IScreen? newScreen)
|
||||||
|
{
|
||||||
|
if (newScreen is IOsuScreen osuScreen && osuScreen.ShowFooter)
|
||||||
|
{
|
||||||
|
screenScreenFooter.Show();
|
||||||
|
screenScreenFooter.SetButtons(osuScreen.CreateFooterButtons());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
screenScreenFooter.Hide();
|
||||||
|
screenScreenFooter.SetButtons(Array.Empty<ScreenFooterButton>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Screens.Menu;
|
||||||
|
using osu.Game.Screens.SelectV2;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.SongSelect
|
||||||
|
{
|
||||||
|
public partial class TestSceneSongSelectV2Navigation : OsuGameTestScene
|
||||||
|
{
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
base.SetUpSteps();
|
||||||
|
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||||
|
AddWaitStep("wait", 5);
|
||||||
|
PushAndConfirm(() => new SongSelectV2());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestClickLogo()
|
||||||
|
{
|
||||||
|
AddStep("click", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(Game.ChildrenOfType<OsuLogo>().Single());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
public TestSceneBackButton()
|
public TestSceneBackButton()
|
||||||
{
|
{
|
||||||
BackButton button;
|
BackButton button;
|
||||||
BackButton.Receptor receptor = new BackButton.Receptor();
|
ScreenFooter.BackReceptor receptor = new ScreenFooter.BackReceptor();
|
||||||
|
|
||||||
Child = new Container
|
Child = new Container
|
||||||
{
|
{
|
||||||
|
@ -2,194 +2,93 @@
|
|||||||
// 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;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
|
||||||
using osu.Game.Screens.Footer;
|
using osu.Game.Screens.Footer;
|
||||||
using osu.Game.Screens.SelectV2.Footer;
|
using osu.Game.Screens.SelectV2.Footer;
|
||||||
using osuTK.Input;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.UserInterface
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
{
|
{
|
||||||
public partial class TestSceneScreenFooter : OsuManualInputManagerTestScene
|
public partial class TestSceneScreenFooter : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
private ScreenFooterButtonRandom randomButton = null!;
|
private ScreenFooter screenFooter = null!;
|
||||||
private ScreenFooterButtonMods modsButton = null!;
|
private TestModSelectOverlay overlay = null!;
|
||||||
|
|
||||||
private bool nextRandomCalled;
|
|
||||||
private bool previousRandomCalled;
|
|
||||||
|
|
||||||
private DummyOverlay overlay = null!;
|
|
||||||
|
|
||||||
[Cached]
|
|
||||||
private OverlayColourProvider colourProvider { get; set; } = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
nextRandomCalled = false;
|
|
||||||
previousRandomCalled = false;
|
|
||||||
|
|
||||||
ScreenFooter footer;
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
overlay = new TestModSelectOverlay
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Bottom = ScreenFooter.HEIGHT
|
||||||
|
}
|
||||||
|
},
|
||||||
new PopoverContainer
|
new PopoverContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = footer = new ScreenFooter(),
|
Child = screenFooter = new ScreenFooter(),
|
||||||
},
|
},
|
||||||
overlay = new DummyOverlay()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
footer.AddButton(modsButton = new ScreenFooterButtonMods { Current = SelectedMods }, overlay);
|
screenFooter.SetButtons(new ScreenFooterButton[]
|
||||||
footer.AddButton(randomButton = new ScreenFooterButtonRandom
|
|
||||||
{
|
{
|
||||||
NextRandom = () => nextRandomCalled = true,
|
new ScreenFooterButtonMods(overlay) { Current = SelectedMods },
|
||||||
PreviousRandom = () => previousRandomCalled = true
|
new ScreenFooterButtonRandom(),
|
||||||
|
new ScreenFooterButtonOptions(),
|
||||||
});
|
});
|
||||||
footer.AddButton(new ScreenFooterButtonOptions());
|
|
||||||
|
|
||||||
overlay.Hide();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public void SetUpSteps()
|
public void SetUpSteps()
|
||||||
{
|
{
|
||||||
AddStep("clear mods", () => SelectedMods.Value = Array.Empty<Mod>());
|
AddStep("show footer", () => screenFooter.Show());
|
||||||
AddStep("set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transition when moving from a screen with no buttons to a screen with buttons.
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestMods()
|
public void TestButtonsIn()
|
||||||
{
|
{
|
||||||
AddStep("one mod", () => SelectedMods.Value = new List<Mod> { new OsuModHidden() });
|
|
||||||
AddStep("two mods", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock() });
|
|
||||||
AddStep("three mods", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime() });
|
|
||||||
AddStep("four mods", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic() });
|
|
||||||
AddStep("five mods", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic(), new OsuModDifficultyAdjust() });
|
|
||||||
|
|
||||||
AddStep("modified", () => SelectedMods.Value = new List<Mod> { new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } });
|
|
||||||
AddStep("modified + one", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } });
|
|
||||||
AddStep("modified + two", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } });
|
|
||||||
AddStep("modified + three", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModClassic(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } });
|
|
||||||
AddStep("modified + four", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModClassic(), new OsuModDifficultyAdjust(), new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } });
|
|
||||||
|
|
||||||
AddStep("clear mods", () => SelectedMods.Value = Array.Empty<Mod>());
|
|
||||||
AddWaitStep("wait", 3);
|
|
||||||
AddStep("one mod", () => SelectedMods.Value = new List<Mod> { new OsuModHidden() });
|
|
||||||
|
|
||||||
AddStep("clear mods", () => SelectedMods.Value = Array.Empty<Mod>());
|
|
||||||
AddWaitStep("wait", 3);
|
|
||||||
AddStep("five mods", () => SelectedMods.Value = new List<Mod> { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime(), new OsuModClassic(), new OsuModDifficultyAdjust() });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transition when moving from a screen with buttons to a screen with no buttons.
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestShowOptions()
|
public void TestButtonsOut()
|
||||||
{
|
{
|
||||||
AddStep("enable options", () =>
|
AddStep("clear buttons", () => screenFooter.SetButtons(Array.Empty<ScreenFooterButton>()));
|
||||||
{
|
|
||||||
var optionsButton = this.ChildrenOfType<ScreenFooterButton>().Last();
|
|
||||||
|
|
||||||
optionsButton.Enabled.Value = true;
|
|
||||||
optionsButton.TriggerClick();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transition when moving from a screen with buttons to a screen with buttons.
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestState()
|
public void TestReplaceButtons()
|
||||||
{
|
{
|
||||||
AddToggleStep("set options enabled state", state => this.ChildrenOfType<ScreenFooterButton>().Last().Enabled.Value = state);
|
AddStep("replace buttons", () => screenFooter.SetButtons(new[]
|
||||||
|
{
|
||||||
|
new ScreenFooterButton { Text = "One", Action = () => { } },
|
||||||
|
new ScreenFooterButton { Text = "Two", Action = () => { } },
|
||||||
|
new ScreenFooterButton { Text = "Three", Action = () => { } },
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
private partial class TestModSelectOverlay : UserModSelectOverlay
|
||||||
public void TestFooterRandom()
|
|
||||||
{
|
{
|
||||||
AddStep("press F2", () => InputManager.Key(Key.F2));
|
protected override bool ShowPresets => true;
|
||||||
AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
public TestModSelectOverlay()
|
||||||
public void TestFooterRandomViaMouse()
|
: base(OverlayColourScheme.Aquamarine)
|
||||||
{
|
{
|
||||||
AddStep("click button", () =>
|
|
||||||
{
|
|
||||||
InputManager.MoveMouseTo(randomButton);
|
|
||||||
InputManager.Click(MouseButton.Left);
|
|
||||||
});
|
|
||||||
AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestFooterRewind()
|
|
||||||
{
|
|
||||||
AddStep("press Shift+F2", () =>
|
|
||||||
{
|
|
||||||
InputManager.PressKey(Key.LShift);
|
|
||||||
InputManager.PressKey(Key.F2);
|
|
||||||
InputManager.ReleaseKey(Key.F2);
|
|
||||||
InputManager.ReleaseKey(Key.LShift);
|
|
||||||
});
|
|
||||||
AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestFooterRewindViaShiftMouseLeft()
|
|
||||||
{
|
|
||||||
AddStep("shift + click button", () =>
|
|
||||||
{
|
|
||||||
InputManager.PressKey(Key.LShift);
|
|
||||||
InputManager.MoveMouseTo(randomButton);
|
|
||||||
InputManager.Click(MouseButton.Left);
|
|
||||||
InputManager.ReleaseKey(Key.LShift);
|
|
||||||
});
|
|
||||||
AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestFooterRewindViaMouseRight()
|
|
||||||
{
|
|
||||||
AddStep("right click button", () =>
|
|
||||||
{
|
|
||||||
InputManager.MoveMouseTo(randomButton);
|
|
||||||
InputManager.Click(MouseButton.Right);
|
|
||||||
});
|
|
||||||
AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestOverlayPresent()
|
|
||||||
{
|
|
||||||
AddStep("Press F1", () =>
|
|
||||||
{
|
|
||||||
InputManager.MoveMouseTo(modsButton);
|
|
||||||
InputManager.Click(MouseButton.Left);
|
|
||||||
});
|
|
||||||
AddAssert("Overlay visible", () => overlay.State.Value == Visibility.Visible);
|
|
||||||
AddStep("Hide", () => overlay.Hide());
|
|
||||||
}
|
|
||||||
|
|
||||||
private partial class DummyOverlay : ShearedOverlayContainer
|
|
||||||
{
|
|
||||||
public DummyOverlay()
|
|
||||||
: base(OverlayColourScheme.Green)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
Header.Title = "An overlay";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Screens.SelectV2.Footer;
|
using osu.Game.Screens.SelectV2.Footer;
|
||||||
@ -26,12 +27,12 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
public TestSceneScreenFooterButtonMods()
|
public TestSceneScreenFooterButtonMods()
|
||||||
{
|
{
|
||||||
Add(footerButtonMods = new TestScreenFooterButtonMods
|
Add(footerButtonMods = new TestScreenFooterButtonMods(new TestModSelectOverlay())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
X = -100,
|
|
||||||
Action = () => { },
|
Action = () => { },
|
||||||
|
X = -100,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,9 +113,24 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
return expectedValue == footerButtonMods.MultiplierText.Current.Value;
|
return expectedValue == footerButtonMods.MultiplierText.Current.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private partial class TestModSelectOverlay : UserModSelectOverlay
|
||||||
|
{
|
||||||
|
protected override bool ShowPresets => true;
|
||||||
|
|
||||||
|
public TestModSelectOverlay()
|
||||||
|
: base(OverlayColourScheme.Aquamarine)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private partial class TestScreenFooterButtonMods : ScreenFooterButtonMods
|
private partial class TestScreenFooterButtonMods : ScreenFooterButtonMods
|
||||||
{
|
{
|
||||||
public new OsuSpriteText MultiplierText => base.MultiplierText;
|
public new OsuSpriteText MultiplierText => base.MultiplierText;
|
||||||
|
|
||||||
|
public TestScreenFooterButtonMods(ModSelectOverlay overlay)
|
||||||
|
: base(overlay)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,12 @@ namespace osu.Game.Tournament.Screens.MapPool
|
|||||||
|
|
||||||
private void beatmapChanged(ValueChangedEvent<TournamentBeatmap?> beatmap)
|
private void beatmapChanged(ValueChangedEvent<TournamentBeatmap?> beatmap)
|
||||||
{
|
{
|
||||||
if (CurrentMatch.Value == null || CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2)
|
if (CurrentMatch.Value?.Round.Value == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int totalBansRequired = CurrentMatch.Value.Round.Value.BanCount.Value * 2;
|
||||||
|
|
||||||
|
if (CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < totalBansRequired)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// if bans have already been placed, beatmap changes result in a selection being made automatically
|
// if bans have already been placed, beatmap changes result in a selection being made automatically
|
||||||
|
@ -7,19 +7,18 @@ using System;
|
|||||||
using osu.Framework.Allocation;
|
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.Input.Bindings;
|
using osu.Game.Screens.Footer;
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Game.Input.Bindings;
|
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
|
// todo: remove this once all screens migrate to display the new game footer and back button.
|
||||||
public partial class BackButton : VisibilityContainer
|
public partial class BackButton : VisibilityContainer
|
||||||
{
|
{
|
||||||
public Action Action;
|
public Action Action;
|
||||||
|
|
||||||
private readonly TwoLayerButton button;
|
private readonly TwoLayerButton button;
|
||||||
|
|
||||||
public BackButton(Receptor receptor = null)
|
public BackButton(ScreenFooter.BackReceptor receptor = null)
|
||||||
{
|
{
|
||||||
Size = TwoLayerButton.SIZE_EXTENDED;
|
Size = TwoLayerButton.SIZE_EXTENDED;
|
||||||
|
|
||||||
@ -35,7 +34,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
if (receptor == null)
|
if (receptor == null)
|
||||||
{
|
{
|
||||||
// if a receptor wasn't provided, create our own locally.
|
// if a receptor wasn't provided, create our own locally.
|
||||||
Add(receptor = new Receptor());
|
Add(receptor = new ScreenFooter.BackReceptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
receptor.OnBackPressed = () => button.TriggerClick();
|
receptor.OnBackPressed = () => button.TriggerClick();
|
||||||
@ -59,29 +58,5 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
button.MoveToX(-TwoLayerButton.SIZE_EXTENDED.X / 2, 400, Easing.OutQuint);
|
button.MoveToX(-TwoLayerButton.SIZE_EXTENDED.X / 2, 400, Easing.OutQuint);
|
||||||
button.FadeOut(400, Easing.OutQuint);
|
button.FadeOut(400, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Receptor : Drawable, IKeyBindingHandler<GlobalAction>
|
|
||||||
{
|
|
||||||
public Action OnBackPressed;
|
|
||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
|
||||||
{
|
|
||||||
if (e.Repeat)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (e.Action)
|
|
||||||
{
|
|
||||||
case GlobalAction.Back:
|
|
||||||
OnBackPressed?.Invoke();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,8 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
{
|
{
|
||||||
public partial class DrawableRank : CompositeDrawable
|
public partial class DrawableRank : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly ScoreRank rank;
|
|
||||||
|
|
||||||
public DrawableRank(ScoreRank rank)
|
public DrawableRank(ScoreRank rank)
|
||||||
{
|
{
|
||||||
this.rank = rank;
|
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
FillMode = FillMode.Fit;
|
FillMode = FillMode.Fit;
|
||||||
FillAspectRatio = 2;
|
FillAspectRatio = 2;
|
||||||
@ -57,7 +53,7 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Spacing = new Vector2(-3, 0),
|
Spacing = new Vector2(-3, 0),
|
||||||
Padding = new MarginPadding { Top = 5 },
|
Padding = new MarginPadding { Top = 5 },
|
||||||
Colour = getRankNameColour(),
|
Colour = GetRankNameColour(rank),
|
||||||
Font = OsuFont.Numeric.With(size: 25),
|
Font = OsuFont.Numeric.With(size: 25),
|
||||||
Text = GetRankName(rank),
|
Text = GetRankName(rank),
|
||||||
ShadowColour = Color4.Black.Opacity(0.3f),
|
ShadowColour = Color4.Black.Opacity(0.3f),
|
||||||
@ -74,7 +70,7 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the grade text colour.
|
/// Retrieves the grade text colour.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ColourInfo getRankNameColour()
|
public static ColourInfo GetRankNameColour(ScoreRank rank)
|
||||||
{
|
{
|
||||||
switch (rank)
|
switch (rank)
|
||||||
{
|
{
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#nullable disable
|
using System;
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Transforms;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Online.Leaderboards
|
namespace osu.Game.Online.Leaderboards
|
||||||
{
|
{
|
||||||
public partial class UpdateableRank : ModelBackedDrawable<ScoreRank?>
|
public partial class UpdateableRank : ModelBackedDrawable<ScoreRank?>
|
||||||
{
|
{
|
||||||
|
protected override double TransformDuration => 600;
|
||||||
|
protected override bool TransformImmediately => true;
|
||||||
|
|
||||||
public ScoreRank? Rank
|
public ScoreRank? Rank
|
||||||
{
|
{
|
||||||
get => Model;
|
get => Model;
|
||||||
@ -22,7 +25,17 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
Rank = rank;
|
Rank = rank;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Drawable CreateDrawable(ScoreRank? rank)
|
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad)
|
||||||
|
{
|
||||||
|
return base.CreateDelayedLoadWrapper(createContentFunc, timeBeforeLoad)
|
||||||
|
.With(w =>
|
||||||
|
{
|
||||||
|
w.Anchor = Anchor.Centre;
|
||||||
|
w.Origin = Anchor.Centre;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Drawable? CreateDrawable(ScoreRank? rank)
|
||||||
{
|
{
|
||||||
if (rank.HasValue)
|
if (rank.HasValue)
|
||||||
{
|
{
|
||||||
@ -35,5 +48,18 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override TransformSequence<Drawable> ApplyShowTransforms(Drawable drawable)
|
||||||
|
{
|
||||||
|
drawable.ScaleTo(1);
|
||||||
|
return base.ApplyShowTransforms(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TransformSequence<Drawable> ApplyHideTransforms(Drawable drawable)
|
||||||
|
{
|
||||||
|
drawable.ScaleTo(1.8f, TransformDuration, Easing.Out);
|
||||||
|
|
||||||
|
return base.ApplyHideTransforms(drawable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
|
|||||||
using osu.Framework.Extensions.TypeExtensions;
|
using osu.Framework.Extensions.TypeExtensions;
|
||||||
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.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
@ -58,6 +59,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -153,6 +155,8 @@ namespace osu.Game
|
|||||||
|
|
||||||
private float toolbarOffset => (Toolbar?.Position.Y ?? 0) + (Toolbar?.DrawHeight ?? 0);
|
private float toolbarOffset => (Toolbar?.Position.Y ?? 0) + (Toolbar?.DrawHeight ?? 0);
|
||||||
|
|
||||||
|
private float screenFooterOffset => (ScreenFooter?.DrawHeight ?? 0) - (ScreenFooter?.Position.Y ?? 0);
|
||||||
|
|
||||||
private IdleTracker idleTracker;
|
private IdleTracker idleTracker;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -177,6 +181,7 @@ namespace osu.Game
|
|||||||
protected OsuScreenStack ScreenStack;
|
protected OsuScreenStack ScreenStack;
|
||||||
|
|
||||||
protected BackButton BackButton;
|
protected BackButton BackButton;
|
||||||
|
protected ScreenFooter ScreenFooter;
|
||||||
|
|
||||||
protected SettingsOverlay Settings;
|
protected SettingsOverlay Settings;
|
||||||
|
|
||||||
@ -926,7 +931,7 @@ namespace osu.Game
|
|||||||
};
|
};
|
||||||
|
|
||||||
Container logoContainer;
|
Container logoContainer;
|
||||||
BackButton.Receptor receptor;
|
ScreenFooter.BackReceptor backReceptor;
|
||||||
|
|
||||||
dependencies.CacheAs(idleTracker = new GameIdleTracker(6000));
|
dependencies.CacheAs(idleTracker = new GameIdleTracker(6000));
|
||||||
|
|
||||||
@ -959,13 +964,20 @@ namespace osu.Game
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
receptor = new BackButton.Receptor(),
|
backReceptor = new ScreenFooter.BackReceptor(),
|
||||||
ScreenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both },
|
ScreenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both },
|
||||||
BackButton = new BackButton(receptor)
|
BackButton = new BackButton(backReceptor)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
Action = () =>
|
Action = () => ScreenFooter.OnBack?.Invoke(),
|
||||||
|
},
|
||||||
|
new PopoverContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = ScreenFooter = new ScreenFooter(backReceptor)
|
||||||
|
{
|
||||||
|
OnBack = () =>
|
||||||
{
|
{
|
||||||
if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen))
|
if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen))
|
||||||
return;
|
return;
|
||||||
@ -974,6 +986,7 @@ namespace osu.Game
|
|||||||
ScreenStack.Exit();
|
ScreenStack.Exit();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
},
|
||||||
logoContainer = new Container { RelativeSizeAxes = Axes.Both },
|
logoContainer = new Container { RelativeSizeAxes = Axes.Both },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -994,6 +1007,8 @@ namespace osu.Game
|
|||||||
new ConfineMouseTracker()
|
new ConfineMouseTracker()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dependencies.Cache(ScreenFooter);
|
||||||
|
|
||||||
ScreenStack.ScreenPushed += screenPushed;
|
ScreenStack.ScreenPushed += screenPushed;
|
||||||
ScreenStack.ScreenExited += screenExited;
|
ScreenStack.ScreenExited += screenExited;
|
||||||
|
|
||||||
@ -1466,6 +1481,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
ScreenOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset };
|
ScreenOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset };
|
||||||
overlayOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset };
|
overlayOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset };
|
||||||
|
ScreenStack.Padding = new MarginPadding { Bottom = screenFooterOffset };
|
||||||
|
|
||||||
float horizontalOffset = 0f;
|
float horizontalOffset = 0f;
|
||||||
|
|
||||||
@ -1538,6 +1554,18 @@ namespace osu.Game
|
|||||||
BackButton.Show();
|
BackButton.Show();
|
||||||
else
|
else
|
||||||
BackButton.Hide();
|
BackButton.Hide();
|
||||||
|
|
||||||
|
if (newOsuScreen.ShowFooter)
|
||||||
|
{
|
||||||
|
BackButton.Hide();
|
||||||
|
ScreenFooter.SetButtons(newOsuScreen.CreateFooterButtons());
|
||||||
|
ScreenFooter.Show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ScreenFooter.SetButtons(Array.Empty<ScreenFooterButton>());
|
||||||
|
ScreenFooter.Hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
skinEditor.SetTarget((OsuScreen)newScreen);
|
skinEditor.SetTarget((OsuScreen)newScreen);
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
public BindableBool Active { get; } = new BindableBool();
|
public BindableBool Active { get; } = new BindableBool();
|
||||||
|
|
||||||
public const float DEFAULT_HEIGHT = 30;
|
public const float DEFAULT_HEIGHT = 30;
|
||||||
private const float width = 73;
|
public const float WIDTH = 73;
|
||||||
|
|
||||||
protected readonly IMod Mod;
|
protected readonly IMod Mod;
|
||||||
private readonly bool showExtendedInformation;
|
private readonly bool showExtendedInformation;
|
||||||
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
Width = 100 + DEFAULT_HEIGHT / 2,
|
Width = 100 + DEFAULT_HEIGHT / 2,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
X = width,
|
X = WIDTH,
|
||||||
Margin = new MarginPadding { Left = -DEFAULT_HEIGHT },
|
Margin = new MarginPadding { Left = -DEFAULT_HEIGHT },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
},
|
},
|
||||||
new CircularContainer
|
new CircularContainer
|
||||||
{
|
{
|
||||||
Width = width,
|
Width = WIDTH,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
|
@ -1,38 +1,175 @@
|
|||||||
// 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.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
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.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Screens.Menu;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Footer
|
namespace osu.Game.Screens.Footer
|
||||||
{
|
{
|
||||||
public partial class ScreenFooter : InputBlockingContainer
|
public partial class ScreenFooter : OverlayContainer
|
||||||
{
|
{
|
||||||
private const int height = 60;
|
|
||||||
private const int padding = 60;
|
private const int padding = 60;
|
||||||
|
private const float delay_per_button = 30;
|
||||||
|
|
||||||
|
public const int HEIGHT = 60;
|
||||||
|
|
||||||
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
|
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
|
||||||
|
|
||||||
/// <param name="button">The button to be added.</param>
|
private ScreenBackButton backButton = null!;
|
||||||
/// <param name="overlay">The <see cref="OverlayContainer"/> to be toggled by this button.</param>
|
private FillFlowContainer<ScreenFooterButton> buttonsFlow = null!;
|
||||||
public void AddButton(ScreenFooterButton button, OverlayContainer? overlay = null)
|
private Container<ScreenFooterButton> removedButtonsContainer = null!;
|
||||||
|
private LogoTrackingContainer logoTrackingContainer = null!;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
|
public Action? OnBack;
|
||||||
|
|
||||||
|
public ScreenFooter(BackReceptor? receptor = null)
|
||||||
{
|
{
|
||||||
if (overlay != null)
|
RelativeSizeAxes = Axes.X;
|
||||||
{
|
Height = HEIGHT;
|
||||||
overlays.Add(overlay);
|
Anchor = Anchor.BottomLeft;
|
||||||
button.Action = () => showOverlay(overlay);
|
Origin = Anchor.BottomLeft;
|
||||||
button.OverlayState.BindTo(overlay.State);
|
|
||||||
|
if (receptor == null)
|
||||||
|
Add(receptor = new BackReceptor());
|
||||||
|
|
||||||
|
receptor.OnBackPressed = () => backButton.TriggerClick();
|
||||||
}
|
}
|
||||||
|
|
||||||
buttons.Add(button);
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background5
|
||||||
|
},
|
||||||
|
buttonsFlow = new FillFlowContainer<ScreenFooterButton>
|
||||||
|
{
|
||||||
|
Margin = new MarginPadding { Left = 12f + ScreenBackButton.BUTTON_WIDTH + padding },
|
||||||
|
Y = 10f,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(7, 0),
|
||||||
|
AutoSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
backButton = new ScreenBackButton
|
||||||
|
{
|
||||||
|
Margin = new MarginPadding { Bottom = 10f, Left = 12f },
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Action = () => OnBack?.Invoke(),
|
||||||
|
},
|
||||||
|
removedButtonsContainer = new Container<ScreenFooterButton>
|
||||||
|
{
|
||||||
|
Margin = new MarginPadding { Left = 12f + ScreenBackButton.BUTTON_WIDTH + padding },
|
||||||
|
Y = 10f,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
(logoTrackingContainer = new LogoTrackingContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}).WithChild(logoTrackingContainer.LogoFacade.With(f =>
|
||||||
|
{
|
||||||
|
f.Anchor = Anchor.BottomRight;
|
||||||
|
f.Origin = Anchor.Centre;
|
||||||
|
f.Position = new Vector2(-76, -36);
|
||||||
|
})),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void StartTrackingLogo(OsuLogo logo, float duration = 0, Easing easing = Easing.None) => logoTrackingContainer.StartTracking(logo, duration, easing);
|
||||||
|
public void StopTrackingLogo() => logoTrackingContainer.StopTracking();
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
this.MoveToY(0, 400, Easing.OutQuint)
|
||||||
|
.FadeIn(400, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
this.MoveToY(HEIGHT, 400, Easing.OutQuint)
|
||||||
|
.FadeOut(400, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetButtons(IReadOnlyList<ScreenFooterButton> buttons)
|
||||||
|
{
|
||||||
|
overlays.Clear();
|
||||||
|
|
||||||
|
var oldButtons = buttonsFlow.ToArray();
|
||||||
|
|
||||||
|
for (int i = 0; i < oldButtons.Length; i++)
|
||||||
|
{
|
||||||
|
var oldButton = oldButtons[i];
|
||||||
|
|
||||||
|
buttonsFlow.Remove(oldButton, false);
|
||||||
|
removedButtonsContainer.Add(oldButton);
|
||||||
|
|
||||||
|
if (buttons.Count > 0)
|
||||||
|
makeButtonDisappearToRightAndExpire(oldButton, i, oldButtons.Length);
|
||||||
|
else
|
||||||
|
makeButtonDisappearToBottomAndExpire(oldButton, i, oldButtons.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buttons.Count; i++)
|
||||||
|
{
|
||||||
|
var newButton = buttons[i];
|
||||||
|
|
||||||
|
if (newButton.Overlay != null)
|
||||||
|
{
|
||||||
|
newButton.Action = () => showOverlay(newButton.Overlay);
|
||||||
|
overlays.Add(newButton.Overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert(!newButton.IsLoaded);
|
||||||
|
buttonsFlow.Add(newButton);
|
||||||
|
|
||||||
|
int index = i;
|
||||||
|
|
||||||
|
// ensure transforms are added after LoadComplete to not be aborted by the FinishTransforms call.
|
||||||
|
newButton.OnLoadComplete += _ =>
|
||||||
|
{
|
||||||
|
if (oldButtons.Length > 0)
|
||||||
|
makeButtonAppearFromLeft(newButton, index, buttons.Count, 240);
|
||||||
|
else
|
||||||
|
makeButtonAppearFromBottom(newButton, index);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeButtonAppearFromLeft(ScreenFooterButton button, int index, int count, float startDelay)
|
||||||
|
=> button.AppearFromLeft(startDelay + (count - index) * delay_per_button);
|
||||||
|
|
||||||
|
private void makeButtonAppearFromBottom(ScreenFooterButton button, int index)
|
||||||
|
=> button.AppearFromBottom(index * delay_per_button);
|
||||||
|
|
||||||
|
private void makeButtonDisappearToRightAndExpire(ScreenFooterButton button, int index, int count)
|
||||||
|
=> button.DisappearToRightAndExpire((count - index) * delay_per_button);
|
||||||
|
|
||||||
|
private void makeButtonDisappearToBottomAndExpire(ScreenFooterButton button, int index, int count)
|
||||||
|
=> button.DisappearToBottomAndExpire((count - index) * delay_per_button);
|
||||||
|
|
||||||
private void showOverlay(OverlayContainer overlay)
|
private void showOverlay(OverlayContainer overlay)
|
||||||
{
|
{
|
||||||
foreach (var o in overlays)
|
foreach (var o in overlays)
|
||||||
@ -44,43 +181,28 @@ namespace osu.Game.Screens.Footer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FillFlowContainer<ScreenFooterButton> buttons = null!;
|
public partial class BackReceptor : Drawable, IKeyBindingHandler<GlobalAction>
|
||||||
|
|
||||||
public ScreenFooter()
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
public Action? OnBackPressed;
|
||||||
Height = height;
|
|
||||||
Anchor = Anchor.BottomLeft;
|
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
Origin = Anchor.BottomLeft;
|
{
|
||||||
|
if (e.Repeat)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (e.Action)
|
||||||
|
{
|
||||||
|
case GlobalAction.Back:
|
||||||
|
OnBackPressed?.Invoke();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
return false;
|
||||||
private void load(OverlayColourProvider colourProvider)
|
}
|
||||||
|
|
||||||
|
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
}
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = colourProvider.Background5
|
|
||||||
},
|
|
||||||
buttons = new FillFlowContainer<ScreenFooterButton>
|
|
||||||
{
|
|
||||||
Position = new Vector2(ScreenBackButton.BUTTON_WIDTH + padding, 10),
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Spacing = new Vector2(7, 0),
|
|
||||||
AutoSizeAxes = Axes.Both
|
|
||||||
},
|
|
||||||
new ScreenBackButton
|
|
||||||
{
|
|
||||||
Margin = new MarginPadding { Bottom = 10f, Left = 12f },
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Action = () => { },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Screens.Footer
|
|||||||
set => icon.Icon = value;
|
set => icon.Icon = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected LocalisableString Text
|
public LocalisableString Text
|
||||||
{
|
{
|
||||||
set => text.Text = value;
|
set => text.Text = value;
|
||||||
}
|
}
|
||||||
@ -69,11 +69,17 @@ namespace osu.Game.Screens.Footer
|
|||||||
private readonly Box glowBox;
|
private readonly Box glowBox;
|
||||||
private readonly Box flashLayer;
|
private readonly Box flashLayer;
|
||||||
|
|
||||||
public ScreenFooterButton()
|
public readonly OverlayContainer? Overlay;
|
||||||
|
|
||||||
|
public ScreenFooterButton(OverlayContainer? overlay = null)
|
||||||
{
|
{
|
||||||
|
Overlay = overlay;
|
||||||
|
|
||||||
Size = new Vector2(BUTTON_WIDTH, BUTTON_HEIGHT);
|
Size = new Vector2(BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||||
|
|
||||||
Child = new Container
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
{
|
{
|
||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
@ -150,6 +156,7 @@ namespace osu.Game.Screens.Footer
|
|||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +164,9 @@ namespace osu.Game.Screens.Footer
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
|
if (Overlay != null)
|
||||||
|
OverlayState.BindTo(Overlay.State);
|
||||||
|
|
||||||
OverlayState.BindValueChanged(_ => updateDisplay());
|
OverlayState.BindValueChanged(_ => updateDisplay());
|
||||||
Enabled.BindValueChanged(_ => updateDisplay(), true);
|
Enabled.BindValueChanged(_ => updateDisplay(), true);
|
||||||
|
|
||||||
@ -215,5 +225,41 @@ namespace osu.Game.Screens.Footer
|
|||||||
|
|
||||||
glowBox.FadeColour(ColourInfo.GradientVertical(buttonAccentColour.Opacity(0f), buttonAccentColour.Opacity(0.2f)), 150, Easing.OutQuint);
|
glowBox.FadeColour(ColourInfo.GradientVertical(buttonAccentColour.Opacity(0f), buttonAccentColour.Opacity(0.2f)), 150, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AppearFromLeft(double delay)
|
||||||
|
{
|
||||||
|
Content.MoveToX(-300f)
|
||||||
|
.FadeOut()
|
||||||
|
.Delay(delay)
|
||||||
|
.MoveToX(0f, 240, Easing.OutCubic)
|
||||||
|
.FadeIn(240, Easing.OutCubic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AppearFromBottom(double delay)
|
||||||
|
{
|
||||||
|
Content.MoveToY(100f)
|
||||||
|
.FadeOut()
|
||||||
|
.Delay(delay)
|
||||||
|
.MoveToY(0f, 240, Easing.OutCubic)
|
||||||
|
.FadeIn(240, Easing.OutCubic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisappearToRightAndExpire(double delay)
|
||||||
|
{
|
||||||
|
Content.Delay(delay)
|
||||||
|
.FadeOut(240, Easing.InOutCubic)
|
||||||
|
.MoveToX(300f, 360, Easing.InOutCubic);
|
||||||
|
|
||||||
|
this.Delay(Content.LatestTransformEndTime - Time.Current).Expire();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisappearToBottomAndExpire(double delay)
|
||||||
|
{
|
||||||
|
Content.Delay(delay)
|
||||||
|
.FadeOut(240, Easing.InOutCubic)
|
||||||
|
.MoveToY(100f, 240, Easing.InOutCubic);
|
||||||
|
|
||||||
|
this.Delay(Content.LatestTransformEndTime - Time.Current).Expire();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
// 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.Collections.Generic;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Screens
|
namespace osu.Game.Screens
|
||||||
@ -19,10 +21,18 @@ namespace osu.Game.Screens
|
|||||||
bool DisallowExternalBeatmapRulesetChanges { get; }
|
bool DisallowExternalBeatmapRulesetChanges { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the user can exit this this <see cref="IOsuScreen"/> by pressing the back button.
|
/// Whether the user can exit this <see cref="IOsuScreen"/> by pressing the back button.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool AllowBackButton { get; }
|
bool AllowBackButton { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether a footer (and a back button) should be displayed underneath the screen.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Temporarily, the back button is shown regardless of whether <see cref="AllowBackButton"/> is true.
|
||||||
|
/// </remarks>
|
||||||
|
bool ShowFooter { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether a top-level component should be allowed to exit the current screen to, for example,
|
/// Whether a top-level component should be allowed to exit the current screen to, for example,
|
||||||
/// complete an import. Note that this can be overridden by a user if they specifically request.
|
/// complete an import. Note that this can be overridden by a user if they specifically request.
|
||||||
@ -63,6 +73,11 @@ namespace osu.Game.Screens
|
|||||||
|
|
||||||
Bindable<RulesetInfo> Ruleset { get; }
|
Bindable<RulesetInfo> Ruleset { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of footer buttons to be added to the game footer, or empty to display no buttons.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyList<ScreenFooterButton> CreateFooterButtons();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether mod track adjustments should be applied on entering this screen.
|
/// Whether mod track adjustments should be applied on entering this screen.
|
||||||
/// A <see langword="null"/> value means that the parent screen's value of this setting will be used.
|
/// A <see langword="null"/> value means that the parent screen's value of this setting will be used.
|
||||||
|
@ -173,9 +173,9 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
IsValidMod = IsValidMod
|
IsValidMod = IsValidMod
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override IEnumerable<(FooterButton, OverlayContainer?)> CreateFooterButtons()
|
protected override IEnumerable<(FooterButton, OverlayContainer?)> CreateSongSelectFooterButtons()
|
||||||
{
|
{
|
||||||
var baseButtons = base.CreateFooterButtons().ToList();
|
var baseButtons = base.CreateSongSelectFooterButtons().ToList();
|
||||||
var freeModsButton = new FooterButtonFreeMods(freeModSelectOverlay) { Current = FreeMods };
|
var freeModsButton = new FooterButtonFreeMods(freeModSelectOverlay) { Current = FreeMods };
|
||||||
|
|
||||||
baseButtons.Insert(baseButtons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (freeModsButton, freeModSelectOverlay));
|
baseButtons.Insert(baseButtons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (freeModsButton, freeModSelectOverlay));
|
||||||
|
@ -16,6 +16,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
@ -38,6 +39,8 @@ namespace osu.Game.Screens
|
|||||||
|
|
||||||
public virtual bool AllowBackButton => true;
|
public virtual bool AllowBackButton => true;
|
||||||
|
|
||||||
|
public virtual bool ShowFooter => false;
|
||||||
|
|
||||||
public virtual bool AllowExternalScreenChange => false;
|
public virtual bool AllowExternalScreenChange => false;
|
||||||
|
|
||||||
public virtual bool HideOverlaysOnEnter => false;
|
public virtual bool HideOverlaysOnEnter => false;
|
||||||
@ -141,6 +144,10 @@ namespace osu.Game.Screens
|
|||||||
[Resolved(canBeNull: true)]
|
[Resolved(canBeNull: true)]
|
||||||
private OsuLogo logo { get; set; }
|
private OsuLogo logo { get; set; }
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
[CanBeNull]
|
||||||
|
protected ScreenFooter Footer { get; private set; }
|
||||||
|
|
||||||
protected OsuScreen()
|
protected OsuScreen()
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
@ -298,6 +305,8 @@ namespace osu.Game.Screens
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual BackgroundScreen CreateBackground() => null;
|
protected virtual BackgroundScreen CreateBackground() => null;
|
||||||
|
|
||||||
|
public virtual IReadOnlyList<ScreenFooterButton> CreateFooterButtons() => Array.Empty<ScreenFooterButton>();
|
||||||
|
|
||||||
public virtual bool OnBackButton() => false;
|
public virtual bool OnBackButton() => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,12 @@ namespace osu.Game.Screens
|
|||||||
|
|
||||||
protected float ParallaxAmount => parallaxContainer.ParallaxAmount;
|
protected float ParallaxAmount => parallaxContainer.ParallaxAmount;
|
||||||
|
|
||||||
|
public new MarginPadding Padding
|
||||||
|
{
|
||||||
|
get => base.Padding;
|
||||||
|
set => base.Padding = value;
|
||||||
|
}
|
||||||
|
|
||||||
public OsuScreenStack()
|
public OsuScreenStack()
|
||||||
{
|
{
|
||||||
InternalChild = parallaxContainer = new ParallaxContainer
|
InternalChild = parallaxContainer = new ParallaxContainer
|
||||||
|
45
osu.Game/Screens/Play/HUD/DefaultRankDisplay.cs
Normal file
45
osu.Game/Screens/Play/HUD/DefaultRankDisplay.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Online.Leaderboards;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play.HUD
|
||||||
|
{
|
||||||
|
public partial class DefaultRankDisplay : Container, ISerialisableDrawable
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private ScoreProcessor scoreProcessor { get; set; } = null!;
|
||||||
|
|
||||||
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
private readonly UpdateableRank rank;
|
||||||
|
|
||||||
|
public DefaultRankDisplay()
|
||||||
|
{
|
||||||
|
Size = new Vector2(70, 35);
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
rank = new UpdateableRank(Scoring.ScoreRank.X)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
rank.Rank = scoreProcessor.Rank.Value;
|
||||||
|
|
||||||
|
scoreProcessor.Rank.BindValueChanged(v => rank.Rank = v.NewValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -61,19 +61,19 @@ namespace osu.Game.Screens.Select
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual bool ControlGlobalMusic => true;
|
protected virtual bool ControlGlobalMusic => true;
|
||||||
|
|
||||||
protected virtual bool ShowFooter => true;
|
protected virtual bool ShowSongSelectFooter => true;
|
||||||
|
|
||||||
public override bool? ApplyModTrackAdjustments => true;
|
public override bool? ApplyModTrackAdjustments => true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Can be null if <see cref="ShowFooter"/> is false.
|
/// Can be null if <see cref="ShowSongSelectFooter"/> is false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected BeatmapOptionsOverlay BeatmapOptions { get; private set; } = null!;
|
protected BeatmapOptionsOverlay BeatmapOptions { get; private set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Can be null if <see cref="ShowFooter"/> is false.
|
/// Can be null if <see cref="ShowSongSelectFooter"/> is false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Footer? Footer { get; private set; }
|
protected Footer? SongSelectFooter { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains any panel which is triggered by a footer button.
|
/// Contains any panel which is triggered by a footer button.
|
||||||
@ -168,7 +168,7 @@ namespace osu.Game.Screens.Select
|
|||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
BleedTop = FilterControl.HEIGHT,
|
BleedTop = FilterControl.HEIGHT,
|
||||||
BleedBottom = Footer.HEIGHT,
|
BleedBottom = Select.Footer.HEIGHT,
|
||||||
SelectionChanged = updateSelectedBeatmap,
|
SelectionChanged = updateSelectedBeatmap,
|
||||||
BeatmapSetsChanged = carouselBeatmapsLoaded,
|
BeatmapSetsChanged = carouselBeatmapsLoaded,
|
||||||
FilterApplied = () => Scheduler.AddOnce(updateVisibleBeatmapCount),
|
FilterApplied = () => Scheduler.AddOnce(updateVisibleBeatmapCount),
|
||||||
@ -215,7 +215,7 @@ namespace osu.Game.Screens.Select
|
|||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Top = FilterControl.HEIGHT,
|
Top = FilterControl.HEIGHT,
|
||||||
Bottom = Footer.HEIGHT
|
Bottom = Select.Footer.HEIGHT
|
||||||
},
|
},
|
||||||
Child = new LoadingSpinner(true) { State = { Value = Visibility.Visible } }
|
Child = new LoadingSpinner(true) { State = { Value = Visibility.Visible } }
|
||||||
}
|
}
|
||||||
@ -302,7 +302,7 @@ namespace osu.Game.Screens.Select
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Bottom = Footer.HEIGHT,
|
Bottom = Select.Footer.HEIGHT,
|
||||||
Top = WEDGE_HEIGHT + 70,
|
Top = WEDGE_HEIGHT + 70,
|
||||||
Left = left_area_padding,
|
Left = left_area_padding,
|
||||||
Right = left_area_padding * 2,
|
Right = left_area_padding * 2,
|
||||||
@ -327,7 +327,7 @@ namespace osu.Game.Screens.Select
|
|||||||
modSpeedHotkeyHandler = new ModSpeedHotkeyHandler(),
|
modSpeedHotkeyHandler = new ModSpeedHotkeyHandler(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ShowFooter)
|
if (ShowSongSelectFooter)
|
||||||
{
|
{
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
@ -336,13 +336,13 @@ namespace osu.Game.Screens.Select
|
|||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Bottom = Footer.HEIGHT },
|
Padding = new MarginPadding { Bottom = Select.Footer.HEIGHT },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
BeatmapOptions = new BeatmapOptionsOverlay(),
|
BeatmapOptions = new BeatmapOptionsOverlay(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Footer = new Footer()
|
SongSelectFooter = new Footer()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,10 +350,10 @@ namespace osu.Game.Screens.Select
|
|||||||
// therein it will be registered at the `OsuGame` level to properly function as a blocking overlay.
|
// therein it will be registered at the `OsuGame` level to properly function as a blocking overlay.
|
||||||
LoadComponent(ModSelect = CreateModSelectOverlay());
|
LoadComponent(ModSelect = CreateModSelectOverlay());
|
||||||
|
|
||||||
if (Footer != null)
|
if (SongSelectFooter != null)
|
||||||
{
|
{
|
||||||
foreach (var (button, overlay) in CreateFooterButtons())
|
foreach (var (button, overlay) in CreateSongSelectFooterButtons())
|
||||||
Footer.AddButton(button, overlay);
|
SongSelectFooter.AddButton(button, overlay);
|
||||||
|
|
||||||
BeatmapOptions.AddButton(@"Manage", @"collections", FontAwesome.Solid.Book, colours.Green, () => manageCollectionsDialog?.Show());
|
BeatmapOptions.AddButton(@"Manage", @"collections", FontAwesome.Solid.Book, colours.Green, () => manageCollectionsDialog?.Show());
|
||||||
BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => DeleteBeatmap(Beatmap.Value.BeatmapSetInfo));
|
BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => DeleteBeatmap(Beatmap.Value.BeatmapSetInfo));
|
||||||
@ -387,7 +387,7 @@ namespace osu.Game.Screens.Select
|
|||||||
/// Creates the buttons to be displayed in the footer.
|
/// Creates the buttons to be displayed in the footer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A set of <see cref="FooterButton"/> and an optional <see cref="OverlayContainer"/> which the button opens when pressed.</returns>
|
/// <returns>A set of <see cref="FooterButton"/> and an optional <see cref="OverlayContainer"/> which the button opens when pressed.</returns>
|
||||||
protected virtual IEnumerable<(FooterButton, OverlayContainer?)> CreateFooterButtons() => new (FooterButton, OverlayContainer?)[]
|
protected virtual IEnumerable<(FooterButton, OverlayContainer?)> CreateSongSelectFooterButtons() => new (FooterButton, OverlayContainer?)[]
|
||||||
{
|
{
|
||||||
(new FooterButtonMods { Current = Mods }, ModSelect),
|
(new FooterButtonMods { Current = Mods }, ModSelect),
|
||||||
(new FooterButtonRandom
|
(new FooterButtonRandom
|
||||||
|
@ -20,7 +20,6 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Screens.Select;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -33,18 +32,22 @@ namespace osu.Game.Screens.SelectV2.Footer
|
|||||||
private FillFlowContainer buttonFlow = null!;
|
private FillFlowContainer buttonFlow = null!;
|
||||||
private readonly ScreenFooterButtonOptions footerButton;
|
private readonly ScreenFooterButtonOptions footerButton;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly OverlayColourProvider colourProvider;
|
||||||
|
|
||||||
private WorkingBeatmap beatmapWhenOpening = null!;
|
private WorkingBeatmap beatmapWhenOpening = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
|
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
|
||||||
|
|
||||||
public BeatmapOptionsPopover(ScreenFooterButtonOptions footerButton)
|
public BeatmapOptionsPopover(ScreenFooterButtonOptions footerButton, OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
this.footerButton = footerButton;
|
this.footerButton = footerButton;
|
||||||
|
this.colourProvider = colourProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ManageCollectionsDialog? manageCollectionsDialog, SongSelect? songSelect, OsuColour colours, BeatmapManager? beatmapManager)
|
private void load(ManageCollectionsDialog? manageCollectionsDialog, OsuColour colours, BeatmapManager? beatmapManager)
|
||||||
{
|
{
|
||||||
Content.Padding = new MarginPadding(5);
|
Content.Padding = new MarginPadding(5);
|
||||||
|
|
||||||
@ -61,15 +64,15 @@ namespace osu.Game.Screens.SelectV2.Footer
|
|||||||
addButton(SongSelectStrings.ManageCollections, FontAwesome.Solid.Book, () => manageCollectionsDialog?.Show());
|
addButton(SongSelectStrings.ManageCollections, FontAwesome.Solid.Book, () => manageCollectionsDialog?.Show());
|
||||||
|
|
||||||
addHeader(SongSelectStrings.ForAllDifficulties, beatmapWhenOpening.BeatmapSetInfo.ToString());
|
addHeader(SongSelectStrings.ForAllDifficulties, beatmapWhenOpening.BeatmapSetInfo.ToString());
|
||||||
addButton(SongSelectStrings.DeleteBeatmap, FontAwesome.Solid.Trash, () => songSelect?.DeleteBeatmap(beatmapWhenOpening.BeatmapSetInfo), colours.Red1);
|
addButton(SongSelectStrings.DeleteBeatmap, FontAwesome.Solid.Trash, () => { }, colours.Red1); // songSelect?.DeleteBeatmap(beatmapWhenOpening.BeatmapSetInfo);
|
||||||
|
|
||||||
addHeader(SongSelectStrings.ForSelectedDifficulty, beatmapWhenOpening.BeatmapInfo.DifficultyName);
|
addHeader(SongSelectStrings.ForSelectedDifficulty, beatmapWhenOpening.BeatmapInfo.DifficultyName);
|
||||||
// TODO: make work, and make show "unplayed" or "played" based on status.
|
// TODO: make work, and make show "unplayed" or "played" based on status.
|
||||||
addButton(SongSelectStrings.MarkAsPlayed, FontAwesome.Regular.TimesCircle, null);
|
addButton(SongSelectStrings.MarkAsPlayed, FontAwesome.Regular.TimesCircle, null);
|
||||||
addButton(SongSelectStrings.ClearAllLocalScores, FontAwesome.Solid.Eraser, () => songSelect?.ClearScores(beatmapWhenOpening.BeatmapInfo), colours.Red1);
|
addButton(SongSelectStrings.ClearAllLocalScores, FontAwesome.Solid.Eraser, () => { }, colours.Red1); // songSelect?.ClearScores(beatmapWhenOpening.BeatmapInfo);
|
||||||
|
|
||||||
if (songSelect != null && songSelect.AllowEditing)
|
// if (songSelect != null && songSelect.AllowEditing)
|
||||||
addButton(SongSelectStrings.EditBeatmap, FontAwesome.Solid.PencilAlt, () => songSelect.Edit(beatmapWhenOpening.BeatmapInfo));
|
addButton(SongSelectStrings.EditBeatmap, FontAwesome.Solid.PencilAlt, () => { }); // songSelect.Edit(beatmapWhenOpening.BeatmapInfo);
|
||||||
|
|
||||||
addButton(WebCommonStrings.ButtonsHide.ToSentence(), FontAwesome.Solid.Magic, () => beatmapManager?.Hide(beatmapWhenOpening.BeatmapInfo));
|
addButton(WebCommonStrings.ButtonsHide.ToSentence(), FontAwesome.Solid.Magic, () => beatmapManager?.Hide(beatmapWhenOpening.BeatmapInfo));
|
||||||
}
|
}
|
||||||
@ -83,9 +86,6 @@ namespace osu.Game.Screens.SelectV2.Footer
|
|||||||
beatmap.BindValueChanged(_ => Hide());
|
beatmap.BindValueChanged(_ => Hide());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private OverlayColourProvider overlayColourProvider { get; set; } = null!;
|
|
||||||
|
|
||||||
private void addHeader(LocalisableString text, string? context = null)
|
private void addHeader(LocalisableString text, string? context = null)
|
||||||
{
|
{
|
||||||
var textFlow = new OsuTextFlowContainer
|
var textFlow = new OsuTextFlowContainer
|
||||||
@ -102,7 +102,7 @@ namespace osu.Game.Screens.SelectV2.Footer
|
|||||||
textFlow.NewLine();
|
textFlow.NewLine();
|
||||||
textFlow.AddText(context, t =>
|
textFlow.AddText(context, t =>
|
||||||
{
|
{
|
||||||
t.Colour = overlayColourProvider.Content2;
|
t.Colour = colourProvider.Content2;
|
||||||
t.Font = t.Font.With(size: 13);
|
t.Font = t.Font.With(size: 13);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens.Footer;
|
using osu.Game.Screens.Footer;
|
||||||
using osu.Game.Screens.Play.HUD;
|
using osu.Game.Screens.Play.HUD;
|
||||||
@ -59,6 +60,11 @@ namespace osu.Game.Screens.SelectV2.Footer
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
|
public ScreenFooterButtonMods(ModSelectOverlay overlay)
|
||||||
|
: base(overlay)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
@ -8,12 +8,16 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Screens.Footer;
|
using osu.Game.Screens.Footer;
|
||||||
|
|
||||||
namespace osu.Game.Screens.SelectV2.Footer
|
namespace osu.Game.Screens.SelectV2.Footer
|
||||||
{
|
{
|
||||||
public partial class ScreenFooterButtonOptions : ScreenFooterButton, IHasPopover
|
public partial class ScreenFooterButtonOptions : ScreenFooterButton, IHasPopover
|
||||||
{
|
{
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colour)
|
private void load(OsuColour colour)
|
||||||
{
|
{
|
||||||
@ -25,6 +29,6 @@ namespace osu.Game.Screens.SelectV2.Footer
|
|||||||
Action = this.ShowPopover;
|
Action = this.ShowPopover;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Popover GetPopover() => new BeatmapOptionsPopover(this);
|
public Popover GetPopover() => new BeatmapOptionsPopover(this, colourProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
754
osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs
Normal file
754
osu.Game/Screens/SelectV2/Leaderboards/LeaderboardScoreV2.cs
Normal file
@ -0,0 +1,754 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Layout;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Extensions;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Backgrounds;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Online.Leaderboards;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Select;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Users.Drawables;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.SelectV2.Leaderboards
|
||||||
|
{
|
||||||
|
public partial class LeaderboardScoreV2 : OsuClickableContainer, IHasContextMenu, IHasCustomTooltip<ScoreInfo>
|
||||||
|
{
|
||||||
|
private const float expanded_right_content_width = 210;
|
||||||
|
private const float grade_width = 40;
|
||||||
|
private const float username_min_width = 125;
|
||||||
|
private const float statistics_regular_min_width = 175;
|
||||||
|
private const float statistics_compact_min_width = 100;
|
||||||
|
private const float rank_label_width = 65;
|
||||||
|
private const float rank_label_visibility_width_cutoff = rank_label_width + height + username_min_width + statistics_regular_min_width + expanded_right_content_width;
|
||||||
|
|
||||||
|
private readonly ScoreInfo score;
|
||||||
|
|
||||||
|
private const int height = 60;
|
||||||
|
private const int corner_radius = 10;
|
||||||
|
private const int transition_duration = 200;
|
||||||
|
|
||||||
|
private readonly int? rank;
|
||||||
|
|
||||||
|
private readonly bool isPersonalBest;
|
||||||
|
|
||||||
|
private Colour4 foregroundColour;
|
||||||
|
private Colour4 backgroundColour;
|
||||||
|
private ColourInfo totalScoreBackgroundGradient;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private SongSelect? songSelect { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IDialogOverlay? dialogOverlay { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private ScoreManager scoreManager { get; set; } = null!;
|
||||||
|
|
||||||
|
private Container content = null!;
|
||||||
|
private Box background = null!;
|
||||||
|
private Box foreground = null!;
|
||||||
|
|
||||||
|
private Drawable avatar = null!;
|
||||||
|
private ClickableAvatar innerAvatar = null!;
|
||||||
|
|
||||||
|
private OsuSpriteText nameLabel = null!;
|
||||||
|
private List<ScoreComponentLabel> statisticsLabels = null!;
|
||||||
|
|
||||||
|
private Container rightContent = null!;
|
||||||
|
|
||||||
|
protected Container RankContainer { get; private set; } = null!;
|
||||||
|
private FillFlowContainer flagBadgeAndDateContainer = null!;
|
||||||
|
private FillFlowContainer modsContainer = null!;
|
||||||
|
|
||||||
|
private OsuSpriteText scoreText = null!;
|
||||||
|
private Drawable scoreRank = null!;
|
||||||
|
private Box totalScoreBackground = null!;
|
||||||
|
|
||||||
|
private FillFlowContainer statisticsContainer = null!;
|
||||||
|
private RankLabel rankLabel = null!;
|
||||||
|
private Container rankLabelOverlay = null!;
|
||||||
|
|
||||||
|
public ITooltip<ScoreInfo> GetCustomTooltip() => new LeaderboardScoreTooltip();
|
||||||
|
public virtual ScoreInfo TooltipContent => score;
|
||||||
|
|
||||||
|
public LeaderboardScoreV2(ScoreInfo score, int? rank, bool isPersonalBest = false)
|
||||||
|
{
|
||||||
|
this.score = score;
|
||||||
|
this.rank = rank;
|
||||||
|
this.isPersonalBest = isPersonalBest;
|
||||||
|
|
||||||
|
Shear = new Vector2(OsuGame.SHEAR, 0);
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
var user = score.User;
|
||||||
|
|
||||||
|
foregroundColour = isPersonalBest ? colourProvider.Background1 : colourProvider.Background5;
|
||||||
|
backgroundColour = isPersonalBest ? colourProvider.Background2 : colourProvider.Background4;
|
||||||
|
totalScoreBackgroundGradient = ColourInfo.GradientHorizontal(backgroundColour.Opacity(0), backgroundColour);
|
||||||
|
|
||||||
|
statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s, score)
|
||||||
|
{
|
||||||
|
// ensure statistics container is the correct width when invalidating
|
||||||
|
AlwaysPresent = true,
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
Child = content = new Container
|
||||||
|
{
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = corner_radius,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = backgroundColour
|
||||||
|
},
|
||||||
|
new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Child = rankLabel = new RankLabel(rank)
|
||||||
|
{
|
||||||
|
Width = rank_label_width,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
createCentreContent(user),
|
||||||
|
createRightContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
innerAvatar.OnLoadComplete += d => d.FadeInFromZero(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuConfigManager config { get; set; } = null!;
|
||||||
|
|
||||||
|
private IBindable<ScoringMode> scoringMode { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
scoringMode = config.GetBindable<ScoringMode>(OsuSetting.ScoreDisplayMode);
|
||||||
|
scoringMode.BindValueChanged(s =>
|
||||||
|
{
|
||||||
|
switch (s.NewValue)
|
||||||
|
{
|
||||||
|
case ScoringMode.Standardised:
|
||||||
|
rightContent.Width = 180f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ScoringMode.Classic:
|
||||||
|
rightContent.Width = expanded_right_content_width;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateModDisplay();
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateModDisplay()
|
||||||
|
{
|
||||||
|
int maxMods = scoringMode.Value == ScoringMode.Standardised ? 4 : 5;
|
||||||
|
|
||||||
|
if (score.Mods.Length > 0)
|
||||||
|
{
|
||||||
|
modsContainer.Padding = new MarginPadding { Top = 4f };
|
||||||
|
modsContainer.ChildrenEnumerable = score.Mods.AsOrdered().Take(Math.Min(maxMods, score.Mods.Length)).Select(mod => new ColouredModSwitchTiny(mod)
|
||||||
|
{
|
||||||
|
Scale = new Vector2(0.375f)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (score.Mods.Length > maxMods)
|
||||||
|
{
|
||||||
|
modsContainer.Remove(modsContainer[^1], true);
|
||||||
|
modsContainer.Add(new MoreModSwitchTiny(score.Mods.Length - maxMods + 1)
|
||||||
|
{
|
||||||
|
Scale = new Vector2(0.375f),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Container createCentreContent(APIUser user) => new Container
|
||||||
|
{
|
||||||
|
Name = @"Centre container",
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = corner_radius,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
foreground = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = foregroundColour
|
||||||
|
},
|
||||||
|
new UserCoverBackground
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
User = score.User,
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Colour = ColourInfo.GradientHorizontal(Colour4.White.Opacity(0.5f), Colour4.FromHex(@"222A27").Opacity(1)),
|
||||||
|
},
|
||||||
|
new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
CornerRadius = corner_radius,
|
||||||
|
Masking = true,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
avatar = new DelayedLoadWrapper(
|
||||||
|
innerAvatar = new ClickableAvatar(user)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(1.1f),
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.None,
|
||||||
|
Size = new Vector2(height)
|
||||||
|
},
|
||||||
|
rankLabelOverlay = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Colour4.Black.Opacity(0.5f),
|
||||||
|
},
|
||||||
|
new RankLabel(rank)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Padding = new MarginPadding { Horizontal = corner_radius },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
flagBadgeAndDateContainer = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new UpdateableFlag(user.CountryCode)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Size = new Vector2(24, 16),
|
||||||
|
},
|
||||||
|
new DateLabel(score.Date)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
UseFullGlyphHeight = false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nameLabel = new TruncatingSpriteText
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
||||||
|
Text = user.Username,
|
||||||
|
Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
Child = statisticsContainer = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Name = @"Statistics container",
|
||||||
|
Padding = new MarginPadding { Right = 40 },
|
||||||
|
Spacing = new Vector2(25, 0),
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = statisticsLabels,
|
||||||
|
Alpha = 0,
|
||||||
|
LayoutEasing = Easing.OutQuint,
|
||||||
|
LayoutDuration = transition_duration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
private Container createRightContent() => rightContent = new Container
|
||||||
|
{
|
||||||
|
Name = @"Right content",
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Right = grade_width },
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = ColourInfo.GradientHorizontal(backgroundColour.Opacity(0), OsuColour.ForRank(score.Rank)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = grade_width,
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Colour = OsuColour.ForRank(score.Rank),
|
||||||
|
},
|
||||||
|
new TrianglesV2
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
SpawnRatio = 2,
|
||||||
|
Velocity = 0.7f,
|
||||||
|
Colour = ColourInfo.GradientHorizontal(backgroundColour.Opacity(0), OsuColour.ForRank(score.Rank).Darken(0.2f)),
|
||||||
|
},
|
||||||
|
RankContainer = new Container
|
||||||
|
{
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = grade_width,
|
||||||
|
Child = scoreRank = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Spacing = new Vector2(-2),
|
||||||
|
Colour = DrawableRank.GetRankNameColour(score.Rank),
|
||||||
|
Font = OsuFont.Numeric.With(size: 16),
|
||||||
|
Text = DrawableRank.GetRankName(score.Rank),
|
||||||
|
ShadowColour = Color4.Black.Opacity(0.3f),
|
||||||
|
ShadowOffset = new Vector2(0, 0.08f),
|
||||||
|
Shadow = true,
|
||||||
|
UseFullGlyphHeight = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Padding = new MarginPadding { Right = grade_width },
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = corner_radius,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
totalScoreBackground = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = totalScoreBackgroundGradient,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = ColourInfo.GradientHorizontal(backgroundColour.Opacity(0), OsuColour.ForRank(score.Rank).Opacity(0.5f)),
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Padding = new MarginPadding { Horizontal = corner_radius },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
scoreText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
UseFullGlyphHeight = false,
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
||||||
|
Current = scoreManager.GetBindableTotalScoreString(score),
|
||||||
|
Font = OsuFont.GetFont(size: 30, weight: FontWeight.Light),
|
||||||
|
},
|
||||||
|
modsContainer = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(2f, 0f),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
protected (CaseTransformableString, LocalisableString DisplayAccuracy)[] GetStatistics(ScoreInfo model) => new[]
|
||||||
|
{
|
||||||
|
(BeatmapsetsStrings.ShowScoreboardHeadersCombo.ToUpper(), model.MaxCombo.ToString().Insert(model.MaxCombo.ToString().Length, "x")),
|
||||||
|
(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy.ToUpper(), model.DisplayAccuracy),
|
||||||
|
};
|
||||||
|
|
||||||
|
public override void Show()
|
||||||
|
{
|
||||||
|
foreach (var d in new[] { avatar, nameLabel, scoreText, scoreRank, flagBadgeAndDateContainer, modsContainer }.Concat(statisticsLabels))
|
||||||
|
d.FadeOut();
|
||||||
|
|
||||||
|
Alpha = 0;
|
||||||
|
|
||||||
|
content.MoveToY(75);
|
||||||
|
avatar.MoveToX(75);
|
||||||
|
nameLabel.MoveToX(150);
|
||||||
|
|
||||||
|
this.FadeIn(200);
|
||||||
|
content.MoveToY(0, 800, Easing.OutQuint);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(100))
|
||||||
|
{
|
||||||
|
avatar.FadeIn(300, Easing.OutQuint);
|
||||||
|
nameLabel.FadeIn(350, Easing.OutQuint);
|
||||||
|
|
||||||
|
avatar.MoveToX(0, 300, Easing.OutQuint);
|
||||||
|
nameLabel.MoveToX(0, 350, Easing.OutQuint);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(250))
|
||||||
|
{
|
||||||
|
scoreText.FadeIn(200);
|
||||||
|
scoreRank.FadeIn(200);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(50))
|
||||||
|
{
|
||||||
|
var drawables = new Drawable[] { flagBadgeAndDateContainer, modsContainer }.Concat(statisticsLabels).ToArray();
|
||||||
|
for (int i = 0; i < drawables.Length; i++)
|
||||||
|
drawables[i].FadeIn(100 + i * 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
updateState();
|
||||||
|
return base.OnHover(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
updateState();
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
|
{
|
||||||
|
var lightenedGradient = ColourInfo.GradientHorizontal(backgroundColour.Opacity(0).Lighten(0.2f), backgroundColour.Lighten(0.2f));
|
||||||
|
|
||||||
|
foreground.FadeColour(IsHovered ? foregroundColour.Lighten(0.2f) : foregroundColour, transition_duration, Easing.OutQuint);
|
||||||
|
background.FadeColour(IsHovered ? backgroundColour.Lighten(0.2f) : backgroundColour, transition_duration, Easing.OutQuint);
|
||||||
|
totalScoreBackground.FadeColour(IsHovered ? lightenedGradient : totalScoreBackgroundGradient, transition_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
if (DrawWidth < rank_label_visibility_width_cutoff && IsHovered)
|
||||||
|
rankLabelOverlay.FadeIn(transition_duration, Easing.OutQuint);
|
||||||
|
else
|
||||||
|
rankLabelOverlay.FadeOut(transition_duration, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
|
||||||
|
{
|
||||||
|
Scheduler.AddOnce(() =>
|
||||||
|
{
|
||||||
|
// when width decreases
|
||||||
|
// - hide rank and show rank overlay on avatar when hovered, then
|
||||||
|
// - compact statistics, then
|
||||||
|
// - hide statistics
|
||||||
|
|
||||||
|
if (DrawWidth >= rank_label_visibility_width_cutoff)
|
||||||
|
rankLabel.FadeIn(transition_duration, Easing.OutQuint).MoveToX(0, transition_duration, Easing.OutQuint);
|
||||||
|
else
|
||||||
|
rankLabel.FadeOut(transition_duration, Easing.OutQuint).MoveToX(-rankLabel.DrawWidth, transition_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
if (DrawWidth >= height + username_min_width + statistics_regular_min_width + expanded_right_content_width)
|
||||||
|
{
|
||||||
|
statisticsContainer.FadeIn(transition_duration, Easing.OutQuint).MoveToX(0, transition_duration, Easing.OutQuint);
|
||||||
|
statisticsContainer.Direction = FillDirection.Horizontal;
|
||||||
|
statisticsContainer.ScaleTo(1, transition_duration, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
else if (DrawWidth >= height + username_min_width + statistics_compact_min_width + expanded_right_content_width)
|
||||||
|
{
|
||||||
|
statisticsContainer.FadeIn(transition_duration, Easing.OutQuint).MoveToX(0, transition_duration, Easing.OutQuint);
|
||||||
|
statisticsContainer.Direction = FillDirection.Vertical;
|
||||||
|
statisticsContainer.ScaleTo(0.8f, transition_duration, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
statisticsContainer.FadeOut(transition_duration, Easing.OutQuint).MoveToX(statisticsContainer.DrawWidth, transition_duration, Easing.OutQuint);
|
||||||
|
});
|
||||||
|
|
||||||
|
return base.OnInvalidate(invalidation, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Subclasses
|
||||||
|
|
||||||
|
private partial class DateLabel : DrawableDate
|
||||||
|
{
|
||||||
|
public DateLabel(DateTimeOffset date)
|
||||||
|
: base(date)
|
||||||
|
{
|
||||||
|
Font = OsuFont.GetFont(size: 16, weight: FontWeight.Medium, italics: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string Format() => Date.ToShortRelativeTime(TimeSpan.FromSeconds(30));
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class ScoreComponentLabel : Container
|
||||||
|
{
|
||||||
|
private readonly (LocalisableString Name, LocalisableString Value) statisticInfo;
|
||||||
|
private readonly ScoreInfo score;
|
||||||
|
|
||||||
|
private FillFlowContainer content = null!;
|
||||||
|
public override bool Contains(Vector2 screenSpacePos) => content.Contains(screenSpacePos);
|
||||||
|
|
||||||
|
public ScoreComponentLabel((LocalisableString Name, LocalisableString Value) statisticInfo, ScoreInfo score)
|
||||||
|
{
|
||||||
|
this.statisticInfo = statisticInfo;
|
||||||
|
this.score = score;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours, OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
OsuSpriteText value;
|
||||||
|
Child = content = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Colour = colourProvider.Content2,
|
||||||
|
Text = statisticInfo.Name,
|
||||||
|
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
|
||||||
|
},
|
||||||
|
value = new OsuSpriteText
|
||||||
|
{
|
||||||
|
// We don't want the value setting the horizontal size, since it leads to wonky accuracy container length,
|
||||||
|
// since the accuracy is sometimes longer than its name.
|
||||||
|
BypassAutoSizeAxes = Axes.X,
|
||||||
|
Text = statisticInfo.Value,
|
||||||
|
Font = OsuFont.GetFont(size: 19, weight: FontWeight.Medium),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (score.Combo != score.MaxCombo && statisticInfo.Name == BeatmapsetsStrings.ShowScoreboardHeadersCombo)
|
||||||
|
value.Colour = colours.Lime1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class RankLabel : Container, IHasTooltip
|
||||||
|
{
|
||||||
|
public RankLabel(int? rank)
|
||||||
|
{
|
||||||
|
if (rank >= 1000)
|
||||||
|
TooltipText = $"#{rank:N0}";
|
||||||
|
|
||||||
|
Child = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold, italics: true),
|
||||||
|
Text = rank == null ? "-" : rank.Value.FormatRank().Insert(0, "#")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalisableString TooltipText { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed partial class ColouredModSwitchTiny : ModSwitchTiny, IHasTooltip
|
||||||
|
{
|
||||||
|
private readonly IMod mod;
|
||||||
|
|
||||||
|
public ColouredModSwitchTiny(IMod mod)
|
||||||
|
: base(mod)
|
||||||
|
{
|
||||||
|
this.mod = mod;
|
||||||
|
Active.Value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalisableString TooltipText => (mod as Mod)?.IconTooltip ?? mod.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed partial class MoreModSwitchTiny : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly int count;
|
||||||
|
|
||||||
|
public MoreModSwitchTiny(int count)
|
||||||
|
{
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Size = new Vector2(ModSwitchTiny.WIDTH, ModSwitchTiny.DEFAULT_HEIGHT);
|
||||||
|
|
||||||
|
InternalChild = new CircularContainer
|
||||||
|
{
|
||||||
|
Masking = true,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = OsuColour.Gray(0.2f),
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Shadow = false,
|
||||||
|
Font = OsuFont.Numeric.With(size: 24, weight: FontWeight.Black),
|
||||||
|
Text = $"+{count}",
|
||||||
|
Colour = colours.Yellow,
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Top = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public MenuItem[] ContextMenuItems
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<MenuItem> items = new List<MenuItem>();
|
||||||
|
|
||||||
|
if (score.Mods.Length > 0 && songSelect != null)
|
||||||
|
items.Add(new OsuMenuItem("Use these mods", MenuItemType.Highlighted, () => songSelect.Mods.Value = score.Mods));
|
||||||
|
|
||||||
|
if (score.Files.Count <= 0) return items.ToArray();
|
||||||
|
|
||||||
|
items.Add(new OsuMenuItem(Localisation.CommonStrings.Export, MenuItemType.Standard, () => scoreManager.Export(score)));
|
||||||
|
items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(score))));
|
||||||
|
|
||||||
|
return items.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
145
osu.Game/Screens/SelectV2/SongSelectV2.cs
Normal file
145
osu.Game/Screens/SelectV2/SongSelectV2.cs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
|
using osu.Game.Screens.Footer;
|
||||||
|
using osu.Game.Screens.Menu;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Screens.SelectV2.Footer;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.SelectV2
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This screen is intended to house all components introduced in the new song select design to add transitions and examine the overall look.
|
||||||
|
/// This will be gradually built upon and ultimately replace <see cref="Select.SongSelect"/> once everything is in place.
|
||||||
|
/// </summary>
|
||||||
|
public partial class SongSelectV2 : OsuScreen
|
||||||
|
{
|
||||||
|
private readonly ModSelectOverlay modSelectOverlay = new SoloModSelectOverlay();
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
|
public override bool ShowFooter => true;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AddRangeInternal(new Drawable[]
|
||||||
|
{
|
||||||
|
new PopoverContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
modSelectOverlay,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IReadOnlyList<ScreenFooterButton> CreateFooterButtons() => new ScreenFooterButton[]
|
||||||
|
{
|
||||||
|
new ScreenFooterButtonMods(modSelectOverlay) { Current = Mods },
|
||||||
|
new ScreenFooterButtonRandom(),
|
||||||
|
new ScreenFooterButtonOptions(),
|
||||||
|
};
|
||||||
|
|
||||||
|
public override void OnEntering(ScreenTransitionEvent e)
|
||||||
|
{
|
||||||
|
this.FadeIn();
|
||||||
|
base.OnEntering(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnResuming(ScreenTransitionEvent e)
|
||||||
|
{
|
||||||
|
this.FadeIn();
|
||||||
|
base.OnResuming(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSuspending(ScreenTransitionEvent e)
|
||||||
|
{
|
||||||
|
this.Delay(400).FadeOut();
|
||||||
|
base.OnSuspending(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnExiting(ScreenExitEvent e)
|
||||||
|
{
|
||||||
|
this.Delay(400).FadeOut();
|
||||||
|
return base.OnExiting(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnBackButton()
|
||||||
|
{
|
||||||
|
if (modSelectOverlay.State.Value == Visibility.Visible)
|
||||||
|
{
|
||||||
|
modSelectOverlay.Hide();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
||||||
|
{
|
||||||
|
base.LogoArriving(logo, resuming);
|
||||||
|
|
||||||
|
if (logo.Alpha > 0.8f)
|
||||||
|
Footer?.StartTrackingLogo(logo, 400, Easing.OutQuint);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logo.Hide();
|
||||||
|
logo.ScaleTo(0.2f);
|
||||||
|
Footer?.StartTrackingLogo(logo);
|
||||||
|
}
|
||||||
|
|
||||||
|
logo.FadeIn(240, Easing.OutQuint);
|
||||||
|
logo.ScaleTo(0.4f, 240, Easing.OutQuint);
|
||||||
|
|
||||||
|
logo.Action = () =>
|
||||||
|
{
|
||||||
|
this.Push(new PlayerLoaderV2(() => new SoloPlayer()));
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LogoSuspending(OsuLogo logo)
|
||||||
|
{
|
||||||
|
base.LogoSuspending(logo);
|
||||||
|
Footer?.StopTrackingLogo();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LogoExiting(OsuLogo logo)
|
||||||
|
{
|
||||||
|
base.LogoExiting(logo);
|
||||||
|
Scheduler.AddDelayed(() => Footer?.StopTrackingLogo(), 120);
|
||||||
|
logo.ScaleTo(0.2f, 120, Easing.Out);
|
||||||
|
logo.FadeOut(120, Easing.Out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class SoloModSelectOverlay : ModSelectOverlay
|
||||||
|
{
|
||||||
|
protected override bool ShowPresets => true;
|
||||||
|
|
||||||
|
public SoloModSelectOverlay()
|
||||||
|
: base(OverlayColourScheme.Aquamarine)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class PlayerLoaderV2 : PlayerLoader
|
||||||
|
{
|
||||||
|
public override bool ShowFooter => true;
|
||||||
|
|
||||||
|
public PlayerLoaderV2(Func<Player> createPlayer)
|
||||||
|
: base(createPlayer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
osu.Game/Skinning/LegacyRankDisplay.cs
Normal file
63
osu.Game/Skinning/LegacyRankDisplay.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
public partial class LegacyRankDisplay : CompositeDrawable, ISerialisableDrawable
|
||||||
|
{
|
||||||
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private ScoreProcessor scoreProcessor { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private ISkinSource source { get; set; } = null!;
|
||||||
|
|
||||||
|
private readonly Sprite rank;
|
||||||
|
|
||||||
|
public LegacyRankDisplay()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
AddInternal(rank = new Sprite
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
scoreProcessor.Rank.BindValueChanged(v =>
|
||||||
|
{
|
||||||
|
var texture = source.GetTexture($"ranking-{v.NewValue}-small");
|
||||||
|
|
||||||
|
rank.Texture = texture;
|
||||||
|
|
||||||
|
if (texture != null)
|
||||||
|
{
|
||||||
|
var transientRank = new Sprite
|
||||||
|
{
|
||||||
|
Texture = texture,
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
BypassAutoSizeAxes = Axes.Both,
|
||||||
|
};
|
||||||
|
AddInternal(transientRank);
|
||||||
|
transientRank.FadeOutFromOne(500, Easing.Out)
|
||||||
|
.ScaleTo(new Vector2(1.625f), 500, Easing.Out)
|
||||||
|
.Expire();
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
FinishTransforms(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user