mirror of
https://github.com/ppy/osu.git
synced 2025-01-22 13:45:08 +08:00
Merge branch 'master' into realm-key-binding-store
This commit is contained in:
commit
66efc3c4de
@ -2,12 +2,6 @@
|
|||||||
"version": 1,
|
"version": 1,
|
||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"cake.tool": {
|
|
||||||
"version": "0.35.0",
|
|
||||||
"commands": [
|
|
||||||
"dotnet-cake"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"dotnet-format": {
|
"dotnet-format": {
|
||||||
"version": "3.1.37601",
|
"version": "3.1.37601",
|
||||||
"commands": [
|
"commands": [
|
||||||
@ -27,7 +21,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"codefilesanity": {
|
"codefilesanity": {
|
||||||
"version": "15.0.0",
|
"version": "0.0.36",
|
||||||
"commands": [
|
"commands": [
|
||||||
"CodeFileSanity"
|
"CodeFileSanity"
|
||||||
]
|
]
|
||||||
|
14
.vscode/launch.json
vendored
14
.vscode/launch.json
vendored
@ -113,20 +113,6 @@
|
|||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build benchmarks",
|
"preLaunchTask": "Build benchmarks",
|
||||||
"console": "internalConsole"
|
"console": "internalConsole"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Cake: Debug Script",
|
|
||||||
"type": "coreclr",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${workspaceRoot}/build/tools/Cake.CoreCLR/0.30.0/Cake.dll",
|
|
||||||
"args": [
|
|
||||||
"${workspaceRoot}/build/build.cake",
|
|
||||||
"--debug",
|
|
||||||
"--verbosity=diagnostic"
|
|
||||||
],
|
|
||||||
"cwd": "${workspaceRoot}/build",
|
|
||||||
"stopAtEntry": true,
|
|
||||||
"externalConsole": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,11 @@
|
|||||||
[CmdletBinding()]
|
|
||||||
Param(
|
|
||||||
[string]$Target,
|
|
||||||
[string]$Configuration,
|
|
||||||
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
|
|
||||||
[string]$Verbosity,
|
|
||||||
[switch]$ShowDescription,
|
|
||||||
[Alias("WhatIf", "Noop")]
|
|
||||||
[switch]$DryRun,
|
|
||||||
[Parameter(Position = 0, Mandatory = $false, ValueFromRemainingArguments = $true)]
|
|
||||||
[string[]]$ScriptArgs
|
|
||||||
)
|
|
||||||
|
|
||||||
# Build Cake arguments
|
|
||||||
$cakeArguments = "";
|
|
||||||
if ($Target) { $cakeArguments += "-target=$Target" }
|
|
||||||
if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
|
|
||||||
if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
|
|
||||||
if ($ShowDescription) { $cakeArguments += "-showdescription" }
|
|
||||||
if ($DryRun) { $cakeArguments += "-dryrun" }
|
|
||||||
if ($Experimental) { $cakeArguments += "-experimental" }
|
|
||||||
$cakeArguments += $ScriptArgs
|
|
||||||
|
|
||||||
dotnet tool restore
|
dotnet tool restore
|
||||||
dotnet cake ./build/InspectCode.cake --bootstrap
|
|
||||||
dotnet cake ./build/InspectCode.cake $cakeArguments
|
# Temporarily disabled until the tool is upgraded to 5.0.
|
||||||
|
# The version specified in .config/dotnet-tools.json (3.1.37601) won't run on .NET hosts >=5.0.7.
|
||||||
|
# - cmd: dotnet format --dry-run --check
|
||||||
|
|
||||||
|
dotnet CodeFileSanity
|
||||||
|
dotnet jb inspectcode "osu.Desktop.slnf" --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN
|
||||||
|
dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors
|
||||||
|
|
||||||
exit $LASTEXITCODE
|
exit $LASTEXITCODE
|
6
InspectCode.sh
Executable file
6
InspectCode.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
dotnet tool restore
|
||||||
|
dotnet CodeFileSanity
|
||||||
|
dotnet jb inspectcode "osu.Desktop.slnf" --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN
|
||||||
|
dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors
|
15
appveyor.yml
15
appveyor.yml
@ -1,24 +1,27 @@
|
|||||||
clone_depth: 1
|
clone_depth: 1
|
||||||
version: '{branch}-{build}'
|
version: '{branch}-{build}'
|
||||||
image: Visual Studio 2019
|
image: Visual Studio 2019
|
||||||
|
cache:
|
||||||
|
- '%LOCALAPPDATA%\NuGet\v3-cache -> appveyor.yml'
|
||||||
|
|
||||||
dotnet_csproj:
|
dotnet_csproj:
|
||||||
patch: true
|
patch: true
|
||||||
file: 'osu.Game\osu.Game.csproj' # Use wildcard when it's able to exclude Xamarin projects
|
file: 'osu.Game\osu.Game.csproj' # Use wildcard when it's able to exclude Xamarin projects
|
||||||
version: '0.0.{build}'
|
version: '0.0.{build}'
|
||||||
cache:
|
|
||||||
- '%LOCALAPPDATA%\NuGet\v3-cache -> appveyor.yml'
|
|
||||||
before_build:
|
before_build:
|
||||||
- ps: dotnet --info # Useful when version mismatch between CI and local
|
- cmd: dotnet --info # Useful when version mismatch between CI and local
|
||||||
- ps: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects
|
- cmd: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects
|
||||||
|
|
||||||
build:
|
build:
|
||||||
project: osu.sln
|
project: osu.sln
|
||||||
parallel: true
|
parallel: true
|
||||||
verbosity: minimal
|
verbosity: minimal
|
||||||
publish_nuget: true
|
publish_nuget: true
|
||||||
|
|
||||||
after_build:
|
after_build:
|
||||||
- ps: dotnet tool restore
|
|
||||||
- ps: dotnet format --dry-run --check
|
|
||||||
- ps: .\InspectCode.ps1
|
- ps: .\InspectCode.ps1
|
||||||
|
|
||||||
test:
|
test:
|
||||||
assemblies:
|
assemblies:
|
||||||
except:
|
except:
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.Build.Traversal">
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\osu.Desktop\osu.Desktop.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch.Tests\osu.Game.Rulesets.Catch.Tests.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Mania.Tests\osu.Game.Rulesets.Mania.Tests.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Osu.Tests\osu.Game.Rulesets.Osu.Tests.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko.Tests\osu.Game.Rulesets.Taiko.Tests.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game.Tests\osu.Game.Tests.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game.Tournament.Tests\osu.Game.Tournament.Tests.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game.Tournament\osu.Game.Tournament.csproj" />
|
|
||||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
@ -1,41 +0,0 @@
|
|||||||
#addin "nuget:?package=CodeFileSanity&version=0.0.36"
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// ARGUMENTS
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
var target = Argument("target", "CodeAnalysis");
|
|
||||||
var configuration = Argument("configuration", "Release");
|
|
||||||
|
|
||||||
var rootDirectory = new DirectoryPath("..");
|
|
||||||
var sln = rootDirectory.CombineWithFilePath("osu.sln");
|
|
||||||
var desktopSlnf = rootDirectory.CombineWithFilePath("osu.Desktop.slnf");
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// TASKS
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
Task("InspectCode")
|
|
||||||
.Does(() => {
|
|
||||||
var inspectcodereport = "inspectcodereport.xml";
|
|
||||||
var cacheDir = "inspectcode";
|
|
||||||
var verbosity = AppVeyor.IsRunningOnAppVeyor ? "WARN" : "INFO"; // Don't flood CI output
|
|
||||||
|
|
||||||
DotNetCoreTool(rootDirectory.FullPath,
|
|
||||||
"jb", $@"inspectcode ""{desktopSlnf}"" --output=""{inspectcodereport}"" --caches-home=""{cacheDir}"" --verbosity={verbosity}");
|
|
||||||
DotNetCoreTool(rootDirectory.FullPath, "nvika", $@"parsereport ""{inspectcodereport}"" --treatwarningsaserrors");
|
|
||||||
});
|
|
||||||
|
|
||||||
Task("CodeFileSanity")
|
|
||||||
.Does(() => {
|
|
||||||
ValidateCodeSanity(new ValidateCodeSanitySettings {
|
|
||||||
RootDirectory = rootDirectory.FullPath,
|
|
||||||
IsAppveyorBuild = AppVeyor.IsRunningOnAppVeyor
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Task("CodeAnalysis")
|
|
||||||
.IsDependentOn("CodeFileSanity")
|
|
||||||
.IsDependentOn("InspectCode");
|
|
||||||
|
|
||||||
RunTarget(target);
|
|
@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
[Nuget]
|
|
||||||
Source=https://api.nuget.org/v3/index.json
|
|
||||||
UseInProcessClient=true
|
|
||||||
LoadDependencies=true
|
|
@ -52,6 +52,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.611.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.611.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.611.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.614.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -12,6 +12,7 @@ using osu.Game.Rulesets.Osu.Objects;
|
|||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||||
{
|
{
|
||||||
@ -40,6 +41,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Texture = source.GetTexture("spinner-background"),
|
Texture = source.GetTexture("spinner-background"),
|
||||||
|
Colour = source.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SpinnerBackground)?.Value ?? new Color4(100, 100, 100, 255),
|
||||||
Scale = new Vector2(SPRITE_SCALE),
|
Scale = new Vector2(SPRITE_SCALE),
|
||||||
Y = SPINNER_Y_CENTRE,
|
Y = SPINNER_Y_CENTRE,
|
||||||
},
|
},
|
||||||
|
@ -7,6 +7,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
SliderTrackOverride,
|
SliderTrackOverride,
|
||||||
SliderBorder,
|
SliderBorder,
|
||||||
SliderBall
|
SliderBall,
|
||||||
|
SpinnerBackground,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,6 @@ namespace osu.Game.Tests.Collections.IO
|
|||||||
await importCollectionsFromStream(osu, ms);
|
await importCollectionsFromStream(osu, ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.That(host.UpdateThread.Running, Is.True);
|
|
||||||
Assert.That(exceptionThrown, Is.False);
|
Assert.That(exceptionThrown, Is.False);
|
||||||
Assert.That(osu.CollectionManager.Collections.Count, Is.EqualTo(0));
|
Assert.That(osu.CollectionManager.Collections.Count, Is.EqualTo(0));
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,22 @@
|
|||||||
// 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.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.Leaderboards;
|
using osu.Game.Online.Leaderboards;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
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.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Select.Leaderboards;
|
using osu.Game.Screens.Select.Leaderboards;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -23,32 +30,98 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
[Cached]
|
[Cached]
|
||||||
private readonly DialogOverlay dialogOverlay;
|
private readonly DialogOverlay dialogOverlay;
|
||||||
|
|
||||||
|
private ScoreManager scoreManager;
|
||||||
|
|
||||||
|
private RulesetStore rulesetStore;
|
||||||
|
private BeatmapManager beatmapManager;
|
||||||
|
|
||||||
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
|
{
|
||||||
|
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
|
||||||
|
dependencies.Cache(rulesetStore = new RulesetStore(ContextFactory));
|
||||||
|
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
|
||||||
|
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory));
|
||||||
|
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
public TestSceneBeatmapLeaderboard()
|
public TestSceneBeatmapLeaderboard()
|
||||||
{
|
{
|
||||||
Add(dialogOverlay = new DialogOverlay
|
AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
dialogOverlay = new DialogOverlay
|
||||||
{
|
{
|
||||||
Depth = -1
|
Depth = -1
|
||||||
});
|
},
|
||||||
|
leaderboard = new FailableLeaderboard
|
||||||
Add(leaderboard = new FailableLeaderboard
|
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Size = new Vector2(550f, 450f),
|
Size = new Vector2(550f, 450f),
|
||||||
Scope = BeatmapLeaderboardScope.Global,
|
Scope = BeatmapLeaderboardScope.Global,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLocalScoresDisplay()
|
||||||
|
{
|
||||||
|
BeatmapInfo beatmapInfo = null;
|
||||||
|
|
||||||
|
AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Local);
|
||||||
|
|
||||||
|
AddStep(@"Set beatmap", () =>
|
||||||
|
{
|
||||||
|
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||||
|
beatmapInfo = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First();
|
||||||
|
|
||||||
|
leaderboard.Beatmap = beatmapInfo;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep(@"New Scores", newScores);
|
clearScores();
|
||||||
|
checkCount(0);
|
||||||
|
|
||||||
|
loadMoreScores(() => beatmapInfo);
|
||||||
|
checkCount(10);
|
||||||
|
|
||||||
|
loadMoreScores(() => beatmapInfo);
|
||||||
|
checkCount(20);
|
||||||
|
|
||||||
|
clearScores();
|
||||||
|
checkCount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestGlobalScoresDisplay()
|
||||||
|
{
|
||||||
|
AddStep(@"Set scope", () => leaderboard.Scope = BeatmapLeaderboardScope.Global);
|
||||||
|
AddStep(@"New Scores", () => leaderboard.Scores = generateSampleScores(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPersonalBest()
|
||||||
|
{
|
||||||
AddStep(@"Show personal best", showPersonalBest);
|
AddStep(@"Show personal best", showPersonalBest);
|
||||||
|
AddStep("null personal best position", showPersonalBestWithNullPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPlaceholderStates()
|
||||||
|
{
|
||||||
AddStep(@"Empty Scores", () => leaderboard.SetRetrievalState(PlaceholderState.NoScores));
|
AddStep(@"Empty Scores", () => leaderboard.SetRetrievalState(PlaceholderState.NoScores));
|
||||||
AddStep(@"Network failure", () => leaderboard.SetRetrievalState(PlaceholderState.NetworkFailure));
|
AddStep(@"Network failure", () => leaderboard.SetRetrievalState(PlaceholderState.NetworkFailure));
|
||||||
AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter));
|
AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter));
|
||||||
AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn));
|
AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn));
|
||||||
AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable));
|
AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable));
|
||||||
AddStep(@"None selected", () => leaderboard.SetRetrievalState(PlaceholderState.NoneSelected));
|
AddStep(@"None selected", () => leaderboard.SetRetrievalState(PlaceholderState.NoneSelected));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatmapStates()
|
||||||
|
{
|
||||||
foreach (BeatmapSetOnlineStatus status in Enum.GetValues(typeof(BeatmapSetOnlineStatus)))
|
foreach (BeatmapSetOnlineStatus status in Enum.GetValues(typeof(BeatmapSetOnlineStatus)))
|
||||||
AddStep($"{status} beatmap", () => showBeatmapWithStatus(status));
|
AddStep($"{status} beatmap", () => showBeatmapWithStatus(status));
|
||||||
AddStep("null personal best position", showPersonalBestWithNullPosition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showPersonalBestWithNullPosition()
|
private void showPersonalBestWithNullPosition()
|
||||||
@ -96,9 +169,26 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void newScores()
|
private void loadMoreScores(Func<BeatmapInfo> beatmapInfo)
|
||||||
{
|
{
|
||||||
var scores = new[]
|
AddStep(@"Load new scores via manager", () =>
|
||||||
|
{
|
||||||
|
foreach (var score in generateSampleScores(beatmapInfo()))
|
||||||
|
scoreManager.Import(score).Wait();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearScores()
|
||||||
|
{
|
||||||
|
AddStep("Clear all scores", () => scoreManager.Delete(scoreManager.GetAllUsableScores()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkCount(int expected) =>
|
||||||
|
AddUntilStep("Correct count displayed", () => leaderboard.ChildrenOfType<LeaderboardScore>().Count() == expected);
|
||||||
|
|
||||||
|
private static ScoreInfo[] generateSampleScores(BeatmapInfo beatmap)
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
{
|
{
|
||||||
new ScoreInfo
|
new ScoreInfo
|
||||||
{
|
{
|
||||||
@ -107,6 +197,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
|
Beatmap = beatmap,
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 6602580,
|
Id = 6602580,
|
||||||
@ -125,6 +216,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
|
Beatmap = beatmap,
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 4608074,
|
Id = 4608074,
|
||||||
@ -143,6 +235,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
|
Beatmap = beatmap,
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 1014222,
|
Id = 1014222,
|
||||||
@ -161,6 +254,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
|
Beatmap = beatmap,
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 1541390,
|
Id = 1541390,
|
||||||
@ -179,6 +273,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
|
Beatmap = beatmap,
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 2243452,
|
Id = 2243452,
|
||||||
@ -197,6 +292,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
|
Beatmap = beatmap,
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 2705430,
|
Id = 2705430,
|
||||||
@ -215,6 +311,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
|
Beatmap = beatmap,
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 7151382,
|
Id = 7151382,
|
||||||
@ -233,6 +330,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
|
Beatmap = beatmap,
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 2051389,
|
Id = 2051389,
|
||||||
@ -251,6 +349,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
|
Beatmap = beatmap,
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 6169483,
|
Id = 6169483,
|
||||||
@ -269,6 +368,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
//Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
||||||
|
Beatmap = beatmap,
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 6702666,
|
Id = 6702666,
|
||||||
@ -281,8 +381,6 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
leaderboard.Scores = scores;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showBeatmapWithStatus(BeatmapSetOnlineStatus status)
|
private void showBeatmapWithStatus(BeatmapSetOnlineStatus status)
|
||||||
|
@ -7,17 +7,17 @@ namespace osu.Game.Localisation
|
|||||||
{
|
{
|
||||||
public static class ChatStrings
|
public static class ChatStrings
|
||||||
{
|
{
|
||||||
private const string prefix = "osu.Game.Localisation.Chat";
|
private const string prefix = @"osu.Game.Localisation.Chat";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "chat"
|
/// "chat"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString HeaderTitle => new TranslatableString(getKey("header_title"), "chat");
|
public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"chat");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "join the real-time discussion"
|
/// "join the real-time discussion"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString HeaderDescription => new TranslatableString(getKey("header_description"), "join the real-time discussion");
|
public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"join the real-time discussion");
|
||||||
|
|
||||||
private static string getKey(string key) => $"{prefix}:{key}";
|
private static string getKey(string key) => $"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,12 @@ namespace osu.Game.Localisation
|
|||||||
{
|
{
|
||||||
public static class CommonStrings
|
public static class CommonStrings
|
||||||
{
|
{
|
||||||
private const string prefix = "osu.Game.Localisation.Common";
|
private const string prefix = @"osu.Game.Localisation.Common";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Cancel"
|
/// "Cancel"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString Cancel => new TranslatableString(getKey("cancel"), "Cancel");
|
public static LocalisableString Cancel => new TranslatableString(getKey(@"cancel"), @"Cancel");
|
||||||
|
|
||||||
private static string getKey(string key) => $"{prefix}:{key}";
|
private static string getKey(string key) => $"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,10 @@ namespace osu.Game.Localisation
|
|||||||
{
|
{
|
||||||
public enum Language
|
public enum Language
|
||||||
{
|
{
|
||||||
[Description("English")]
|
[Description(@"English")]
|
||||||
en,
|
en,
|
||||||
|
|
||||||
[Description("日本語")]
|
[Description(@"日本語")]
|
||||||
ja
|
ja
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,17 +7,17 @@ namespace osu.Game.Localisation
|
|||||||
{
|
{
|
||||||
public static class NotificationsStrings
|
public static class NotificationsStrings
|
||||||
{
|
{
|
||||||
private const string prefix = "osu.Game.Localisation.Notifications";
|
private const string prefix = @"osu.Game.Localisation.Notifications";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "notifications"
|
/// "notifications"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString HeaderTitle => new TranslatableString(getKey("header_title"), "notifications");
|
public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"notifications");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "waiting for 'ya"
|
/// "waiting for 'ya"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString HeaderDescription => new TranslatableString(getKey("header_description"), "waiting for 'ya");
|
public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"waiting for 'ya");
|
||||||
|
|
||||||
private static string getKey(string key) => $"{prefix}:{key}";
|
private static string getKey(string key) => $"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
|
@ -7,17 +7,17 @@ namespace osu.Game.Localisation
|
|||||||
{
|
{
|
||||||
public static class NowPlayingStrings
|
public static class NowPlayingStrings
|
||||||
{
|
{
|
||||||
private const string prefix = "osu.Game.Localisation.NowPlaying";
|
private const string prefix = @"osu.Game.Localisation.NowPlaying";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "now playing"
|
/// "now playing"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString HeaderTitle => new TranslatableString(getKey("header_title"), "now playing");
|
public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"now playing");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "manage the currently playing track"
|
/// "manage the currently playing track"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString HeaderDescription => new TranslatableString(getKey("header_description"), "manage the currently playing track");
|
public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"manage the currently playing track");
|
||||||
|
|
||||||
private static string getKey(string key) => $"{prefix}:{key}";
|
private static string getKey(string key) => $"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Resources;
|
using System.Resources;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
@ -34,7 +35,29 @@ namespace osu.Game.Localisation
|
|||||||
lock (resourceManagers)
|
lock (resourceManagers)
|
||||||
{
|
{
|
||||||
if (!resourceManagers.TryGetValue(ns, out var manager))
|
if (!resourceManagers.TryGetValue(ns, out var manager))
|
||||||
resourceManagers[ns] = manager = new ResourceManager(ns, GetType().Assembly);
|
{
|
||||||
|
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||||
|
|
||||||
|
// Traverse backwards through periods in the namespace to find a matching assembly.
|
||||||
|
string assemblyName = ns;
|
||||||
|
|
||||||
|
while (!string.IsNullOrEmpty(assemblyName))
|
||||||
|
{
|
||||||
|
var matchingAssembly = loadedAssemblies.FirstOrDefault(asm => asm.GetName().Name == assemblyName);
|
||||||
|
|
||||||
|
if (matchingAssembly != null)
|
||||||
|
{
|
||||||
|
resourceManagers[ns] = manager = new ResourceManager(ns, matchingAssembly);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastIndex = Math.Max(0, assemblyName.LastIndexOf('.'));
|
||||||
|
assemblyName = assemblyName.Substring(0, lastIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manager == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -7,17 +7,17 @@ namespace osu.Game.Localisation
|
|||||||
{
|
{
|
||||||
public static class SettingsStrings
|
public static class SettingsStrings
|
||||||
{
|
{
|
||||||
private const string prefix = "osu.Game.Localisation.Settings";
|
private const string prefix = @"osu.Game.Localisation.Settings";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "settings"
|
/// "settings"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString HeaderTitle => new TranslatableString(getKey("header_title"), "settings");
|
public static LocalisableString HeaderTitle => new TranslatableString(getKey(@"header_title"), @"settings");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "change the way osu! behaves"
|
/// "change the way osu! behaves"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString HeaderDescription => new TranslatableString(getKey("header_description"), "change the way osu! behaves");
|
public static LocalisableString HeaderDescription => new TranslatableString(getKey(@"header_description"), @"change the way osu! behaves");
|
||||||
|
|
||||||
private static string getKey(string key) => $"{prefix}:{key}";
|
private static string getKey(string key) => $"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
|
@ -44,9 +44,9 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
private IEnumerable<TScoreInfo> scores;
|
private ICollection<TScoreInfo> scores;
|
||||||
|
|
||||||
public IEnumerable<TScoreInfo> Scores
|
public ICollection<TScoreInfo> Scores
|
||||||
{
|
{
|
||||||
get => scores;
|
get => scores;
|
||||||
set
|
set
|
||||||
@ -126,7 +126,7 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
scope = value;
|
scope = value;
|
||||||
UpdateScores();
|
RefreshScores();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
case PlaceholderState.NetworkFailure:
|
case PlaceholderState.NetworkFailure:
|
||||||
replacePlaceholder(new ClickablePlaceholder(@"Couldn't fetch scores!", FontAwesome.Solid.Sync)
|
replacePlaceholder(new ClickablePlaceholder(@"Couldn't fetch scores!", FontAwesome.Solid.Sync)
|
||||||
{
|
{
|
||||||
Action = UpdateScores,
|
Action = RefreshScores
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -254,8 +254,6 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
apiState.BindValueChanged(onlineStateChanged, true);
|
apiState.BindValueChanged(onlineStateChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshScores() => UpdateScores();
|
|
||||||
|
|
||||||
private APIRequest getScoresRequest;
|
private APIRequest getScoresRequest;
|
||||||
|
|
||||||
protected abstract bool IsOnlineScope { get; }
|
protected abstract bool IsOnlineScope { get; }
|
||||||
@ -267,12 +265,14 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
case APIState.Online:
|
case APIState.Online:
|
||||||
case APIState.Offline:
|
case APIState.Offline:
|
||||||
if (IsOnlineScope)
|
if (IsOnlineScope)
|
||||||
UpdateScores();
|
RefreshScores();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
public void RefreshScores() => Scheduler.AddOnce(UpdateScores);
|
||||||
|
|
||||||
protected void UpdateScores()
|
protected void UpdateScores()
|
||||||
{
|
{
|
||||||
// don't display any scores or placeholder until the first Scores_Set has been called.
|
// don't display any scores or placeholder until the first Scores_Set has been called.
|
||||||
@ -290,7 +290,7 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
|
|
||||||
getScoresRequest = FetchScores(scores => Schedule(() =>
|
getScoresRequest = FetchScores(scores => Schedule(() =>
|
||||||
{
|
{
|
||||||
Scores = scores;
|
Scores = scores.ToArray();
|
||||||
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
|
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -2,16 +2,20 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
public class SettingsNumberBox : SettingsItem<string>
|
public class SettingsNumberBox : SettingsItem<string>
|
||||||
{
|
{
|
||||||
protected override Drawable CreateControl() => new OsuNumberBox
|
protected override Drawable CreateControl() => new NumberBox
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding { Top = 5 },
|
Margin = new MarginPadding { Top = 5 },
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public class NumberBox : SettingsTextBox.TextBox
|
||||||
|
{
|
||||||
|
protected override bool CanAddCharacter(char character) => char.IsNumber(character);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,60 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
public class SettingsTextBox : SettingsItem<string>
|
public class SettingsTextBox : SettingsItem<string>
|
||||||
{
|
{
|
||||||
protected override Drawable CreateControl() => new OsuTextBox
|
protected override Drawable CreateControl() => new TextBox
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding { Top = 5 },
|
Margin = new MarginPadding { Top = 5 },
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
CommitOnFocusLost = true,
|
CommitOnFocusLost = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public class TextBox : OsuTextBox
|
||||||
|
{
|
||||||
|
private const float border_thickness = 3;
|
||||||
|
|
||||||
|
private Color4 borderColourFocused;
|
||||||
|
private Color4 borderColourUnfocused;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colour)
|
||||||
|
{
|
||||||
|
borderColourUnfocused = colour.Gray4.Opacity(0.5f);
|
||||||
|
borderColourFocused = BorderColour;
|
||||||
|
|
||||||
|
updateBorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFocus(FocusEvent e)
|
||||||
|
{
|
||||||
|
base.OnFocus(e);
|
||||||
|
|
||||||
|
updateBorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFocusLost(FocusLostEvent e)
|
||||||
|
{
|
||||||
|
base.OnFocusLost(e);
|
||||||
|
|
||||||
|
updateBorder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBorder()
|
||||||
|
{
|
||||||
|
BorderThickness = border_thickness;
|
||||||
|
BorderColour = HasFocus ? borderColourFocused : borderColourUnfocused;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
@ -50,7 +49,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly OsuNumberBox seedNumberBox;
|
private readonly SettingsNumberBox.NumberBox seedNumberBox;
|
||||||
|
|
||||||
public SeedControl()
|
public SeedControl()
|
||||||
{
|
{
|
||||||
@ -76,7 +75,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
{
|
{
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
seedNumberBox = new OsuNumberBox
|
seedNumberBox = new SettingsNumberBox.NumberBox
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
CommitOnFocusLost = true
|
CommitOnFocusLost = true
|
||||||
|
@ -19,6 +19,20 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
private readonly IBindable<double> timeRange = new BindableDouble();
|
private readonly IBindable<double> timeRange = new BindableDouble();
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the scrolling direction is horizontal or vertical.
|
||||||
|
/// </summary>
|
||||||
|
private Direction scrollingAxis => direction.Value == ScrollingDirection.Left || direction.Value == ScrollingDirection.Right ? Direction.Horizontal : Direction.Vertical;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The scrolling axis is inverted if objects temporally farther in the future have a smaller position value across the scrolling axis.
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// <see cref="ScrollingDirection.Down"/> is inverted, because given two objects, one of which is at the current time and one of which is 1000ms in the future,
|
||||||
|
/// in the current time instant the future object is spatially above the current object, and therefore has a smaller value of the Y coordinate of its position.
|
||||||
|
/// </example>
|
||||||
|
private bool axisInverted => direction.Value == ScrollingDirection.Down || direction.Value == ScrollingDirection.Right;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A set of top-level <see cref="DrawableHitObject"/>s which have an up-to-date layout.
|
/// A set of top-level <see cref="DrawableHitObject"/>s which have an up-to-date layout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -48,99 +62,64 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Given a position in screen space, return the time within this column.
|
/// Given a position at <paramref name="currentTime"/>, return the time of the object corresponding to the position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double TimeAtScreenSpacePosition(Vector2 screenSpacePosition)
|
/// <remarks>
|
||||||
|
/// If there are multiple valid time values, one arbitrary time is returned.
|
||||||
|
/// </remarks>
|
||||||
|
public double TimeAtPosition(float localPosition, double currentTime)
|
||||||
{
|
{
|
||||||
// convert to local space of column so we can snap and fetch correct location.
|
float scrollPosition = axisInverted ? scrollLength - localPosition : localPosition;
|
||||||
Vector2 localPosition = ToLocalSpace(screenSpacePosition);
|
return scrollingInfo.Algorithm.TimeAt(scrollPosition, currentTime, timeRange.Value, scrollLength);
|
||||||
|
|
||||||
float position = 0;
|
|
||||||
|
|
||||||
switch (scrollingInfo.Direction.Value)
|
|
||||||
{
|
|
||||||
case ScrollingDirection.Up:
|
|
||||||
case ScrollingDirection.Down:
|
|
||||||
position = localPosition.Y;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ScrollingDirection.Right:
|
|
||||||
case ScrollingDirection.Left:
|
|
||||||
position = localPosition.X;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
flipPositionIfRequired(ref position);
|
|
||||||
|
|
||||||
return scrollingInfo.Algorithm.TimeAt(position, Time.Current, scrollingInfo.TimeRange.Value, scrollLength);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Given a time, return the screen space position within this column.
|
/// Given a position at the current time in screen space, return the time of the object corresponding the position.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If there are multiple valid time values, one arbitrary time is returned.
|
||||||
|
/// </remarks>
|
||||||
|
public double TimeAtScreenSpacePosition(Vector2 screenSpacePosition)
|
||||||
|
{
|
||||||
|
Vector2 localPosition = ToLocalSpace(screenSpacePosition);
|
||||||
|
return TimeAtPosition(scrollingAxis == Direction.Horizontal ? localPosition.X : localPosition.Y, Time.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a time, return the position along the scrolling axis within this <see cref="HitObjectContainer"/> at time <paramref name="currentTime"/>.
|
||||||
|
/// </summary>
|
||||||
|
public float PositionAtTime(double time, double currentTime)
|
||||||
|
{
|
||||||
|
float scrollPosition = scrollingInfo.Algorithm.PositionAt(time, currentTime, timeRange.Value, scrollLength);
|
||||||
|
return axisInverted ? scrollLength - scrollPosition : scrollPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a time, return the position along the scrolling axis within this <see cref="HitObjectContainer"/> at the current time.
|
||||||
|
/// </summary>
|
||||||
|
public float PositionAtTime(double time) => PositionAtTime(time, Time.Current);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a time, return the screen space position within this <see cref="HitObjectContainer"/>.
|
||||||
|
/// In the non-scrolling axis, the center of this <see cref="HitObjectContainer"/> is returned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Vector2 ScreenSpacePositionAtTime(double time)
|
public Vector2 ScreenSpacePositionAtTime(double time)
|
||||||
{
|
{
|
||||||
var pos = scrollingInfo.Algorithm.PositionAt(time, Time.Current, scrollingInfo.TimeRange.Value, scrollLength);
|
float localPosition = PositionAtTime(time, Time.Current);
|
||||||
|
return scrollingAxis == Direction.Horizontal
|
||||||
|
? ToScreenSpace(new Vector2(localPosition, DrawHeight / 2))
|
||||||
|
: ToScreenSpace(new Vector2(DrawWidth / 2, localPosition));
|
||||||
|
}
|
||||||
|
|
||||||
flipPositionIfRequired(ref pos);
|
/// <summary>
|
||||||
|
/// Given a start time and end time of a scrolling object, return the length of the object along the scrolling axis.
|
||||||
switch (scrollingInfo.Direction.Value)
|
/// </summary>
|
||||||
|
public float LengthAtTime(double startTime, double endTime)
|
||||||
{
|
{
|
||||||
case ScrollingDirection.Up:
|
return scrollingInfo.Algorithm.GetLength(startTime, endTime, timeRange.Value, scrollLength);
|
||||||
case ScrollingDirection.Down:
|
|
||||||
return ToScreenSpace(new Vector2(getBreadth() / 2, pos));
|
|
||||||
|
|
||||||
default:
|
|
||||||
return ToScreenSpace(new Vector2(pos, getBreadth() / 2));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private float scrollLength
|
private float scrollLength => scrollingAxis == Direction.Horizontal ? DrawWidth : DrawHeight;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
switch (scrollingInfo.Direction.Value)
|
|
||||||
{
|
|
||||||
case ScrollingDirection.Left:
|
|
||||||
case ScrollingDirection.Right:
|
|
||||||
return DrawWidth;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return DrawHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float getBreadth()
|
|
||||||
{
|
|
||||||
switch (scrollingInfo.Direction.Value)
|
|
||||||
{
|
|
||||||
case ScrollingDirection.Up:
|
|
||||||
case ScrollingDirection.Down:
|
|
||||||
return DrawWidth;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return DrawHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void flipPositionIfRequired(ref float position)
|
|
||||||
{
|
|
||||||
// We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time.
|
|
||||||
// The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position,
|
|
||||||
// so when scrolling downwards the coordinates need to be flipped.
|
|
||||||
|
|
||||||
switch (scrollingInfo.Direction.Value)
|
|
||||||
{
|
|
||||||
case ScrollingDirection.Down:
|
|
||||||
position = DrawHeight - position;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ScrollingDirection.Right:
|
|
||||||
position = DrawWidth - position;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void AddDrawable(HitObjectLifetimeEntry entry, DrawableHitObject drawable)
|
protected override void AddDrawable(HitObjectLifetimeEntry entry, DrawableHitObject drawable)
|
||||||
{
|
{
|
||||||
@ -237,18 +216,11 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
{
|
{
|
||||||
if (hitObject.HitObject is IHasDuration e)
|
if (hitObject.HitObject is IHasDuration e)
|
||||||
{
|
{
|
||||||
switch (direction.Value)
|
float length = LengthAtTime(hitObject.HitObject.StartTime, e.EndTime);
|
||||||
{
|
if (scrollingAxis == Direction.Horizontal)
|
||||||
case ScrollingDirection.Up:
|
hitObject.Width = length;
|
||||||
case ScrollingDirection.Down:
|
else
|
||||||
hitObject.Height = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, scrollLength);
|
hitObject.Height = length;
|
||||||
break;
|
|
||||||
|
|
||||||
case ScrollingDirection.Left:
|
|
||||||
case ScrollingDirection.Right:
|
|
||||||
hitObject.Width = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, scrollLength);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var obj in hitObject.NestedHitObjects)
|
foreach (var obj in hitObject.NestedHitObjects)
|
||||||
@ -262,24 +234,16 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
|
|
||||||
private void updatePosition(DrawableHitObject hitObject, double currentTime)
|
private void updatePosition(DrawableHitObject hitObject, double currentTime)
|
||||||
{
|
{
|
||||||
switch (direction.Value)
|
float position = PositionAtTime(hitObject.HitObject.StartTime, currentTime);
|
||||||
{
|
|
||||||
case ScrollingDirection.Up:
|
|
||||||
hitObject.Y = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ScrollingDirection.Down:
|
// The position returned from `PositionAtTime` is assuming the `TopLeft` anchor.
|
||||||
hitObject.Y = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength);
|
// A correction is needed because the hit objects are using a different anchor for each direction (e.g. `BottomCentre` for `Bottom` direction).
|
||||||
break;
|
float anchorCorrection = axisInverted ? scrollLength : 0;
|
||||||
|
|
||||||
case ScrollingDirection.Left:
|
if (scrollingAxis == Direction.Horizontal)
|
||||||
hitObject.X = scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength);
|
hitObject.X = position - anchorCorrection;
|
||||||
break;
|
else
|
||||||
|
hitObject.Y = position - anchorCorrection;
|
||||||
case ScrollingDirection.Right:
|
|
||||||
hitObject.X = -scrollingInfo.Algorithm.PositionAt(hitObject.HitObject.StartTime, currentTime, timeRange.Value, scrollLength);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
|
|
||||||
private IBindable<WeakReference<ScoreInfo>> itemRemoved;
|
private IBindable<WeakReference<ScoreInfo>> itemRemoved;
|
||||||
|
|
||||||
|
private IBindable<WeakReference<ScoreInfo>> itemAdded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to apply the game's currently selected mods as a filter when retrieving scores.
|
/// Whether to apply the game's currently selected mods as a filter when retrieving scores.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -85,6 +87,9 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
|
|
||||||
itemRemoved = scoreManager.ItemRemoved.GetBoundCopy();
|
itemRemoved = scoreManager.ItemRemoved.GetBoundCopy();
|
||||||
itemRemoved.BindValueChanged(onScoreRemoved);
|
itemRemoved.BindValueChanged(onScoreRemoved);
|
||||||
|
|
||||||
|
itemAdded = scoreManager.ItemUpdated.GetBoundCopy();
|
||||||
|
itemAdded.BindValueChanged(onScoreAdded);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Reset()
|
protected override void Reset()
|
||||||
@ -93,7 +98,25 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
TopScore = null;
|
TopScore = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onScoreRemoved(ValueChangedEvent<WeakReference<ScoreInfo>> score) => Schedule(RefreshScores);
|
private void onScoreRemoved(ValueChangedEvent<WeakReference<ScoreInfo>> score) =>
|
||||||
|
scoreStoreChanged(score);
|
||||||
|
|
||||||
|
private void onScoreAdded(ValueChangedEvent<WeakReference<ScoreInfo>> score) =>
|
||||||
|
scoreStoreChanged(score);
|
||||||
|
|
||||||
|
private void scoreStoreChanged(ValueChangedEvent<WeakReference<ScoreInfo>> score)
|
||||||
|
{
|
||||||
|
if (Scope != BeatmapLeaderboardScope.Local)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (score.NewValue.TryGetTarget(out var scoreInfo))
|
||||||
|
{
|
||||||
|
if (Beatmap?.ID != scoreInfo.BeatmapInfoID)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshScores();
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local;
|
protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local;
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
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.Framework.Input;
|
|
||||||
using osu.Framework.Testing.Input;
|
using osu.Framework.Testing.Input;
|
||||||
using osu.Game.Graphics.Cursor;
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
@ -49,7 +48,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
InputManager = new ManualInputManager
|
InputManager = new ManualInputManager
|
||||||
{
|
{
|
||||||
UseParentInput = true,
|
UseParentInput = true,
|
||||||
Child = new PlatformActionContainer().WithChild(mainContent)
|
Child = mainContent
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.2.0" />
|
<PackageReference Include="Realm" Version="10.2.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.611.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.614.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.611.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.611.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.4.0" />
|
<PackageReference Include="Sentry" Version="3.4.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.611.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.614.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.611.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.611.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
@ -93,7 +93,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.611.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.614.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user