Merge remote-tracking branch 'upstream/master' into peppy-async-screens

This commit is contained in:
Dean Herbert
2018-04-20 16:52:15 +09:00
1087 changed files with 96350 additions and 94769 deletions

41
.gitattributes vendored
View File

@ -1,19 +1,24 @@
# This won't normalise line endings, but it will ensure that merge drivers use CRLF # Autodetect text files and ensure that we normalise their
* -text eol=crlf # line endings to lf internally. When checked out they may
# use different line endings.
* text=auto
# Currently in-use binary file extensions # Check out with crlf (Windows) line endings
*.blend binary *.sln text eol=crlf
*.bmp binary *.csproj text eol=crlf
*.dll binary *.cs text diff=csharp eol=crlf
*.exe binary *.resx text eol=crlf
*.icns binary *.vsixmanifest text eol=crlf
*.ico binary packages.config text eol=crlf
*.jpg binary App.config text eol=crlf
*.osz2 binary *.bat text eol=crlf
*.pdn binary *.cmd text eol=crlf
*.psd binary *.snippet text eol=crlf
*.PSD binary *.manifest text eol=crlf
*.tga binary
*.ttf binary # Check out with lf (UNIX) line endings
*.wav binary *.sh text eol=lf
*.xnb binary .gitignore text eol=lf
.gitattributes text eol=lf
*.md text eol=lf
.travis.yml text eol=lf

46
app.manifest Normal file
View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="1.0.0.0" name="osu!" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</asmv1:assembly>

30
appveyor_deploy.yml Normal file
View File

@ -0,0 +1,30 @@
# 2017-09-14
clone_depth: 1
version: '{branch}-{build}'
image: Visual Studio 2017
configuration: Debug
cache:
- packages -> **\packages.config
install:
- cmd: git submodule update --init --recursive --depth=5
before_build:
- cmd: nuget restore -verbosity quiet
build:
project: osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
verbosity: minimal
after_build:
- ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1'))
- appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate
- cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx
- appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration
- cmd: appveyor-tools\secure-file -decrypt fdc6f19b04.enc -secret %decode_secret% -out osu.Desktop.Deploy\bin\Debug\net461\osu.Desktop.Deploy.exe.config
- cd osu.Desktop.Deploy\bin\Debug\net461\
- osu.Desktop.Deploy.exe %code_signing_password%
environment:
TargetFramework: net461
decode_secret:
secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY=
code_signing_password:
secure: 34tLNqvjmmZEi97MLKfrnQ==
artifacts:
- path: 'Releases\*'

View File

@ -57,8 +57,12 @@ namespace osu.Desktop.Deploy
private static string codeSigningPassword; private static string codeSigningPassword;
private static bool interactive;
public static void Main(string[] args) public static void Main(string[] args)
{ {
interactive = args.Length == 0;
displayHeader(); displayHeader();
findSolutionPath(); findSolutionPath();
@ -82,15 +86,15 @@ namespace osu.Desktop.Deploy
string version = $"{verBase}{increment}"; string version = $"{verBase}{increment}";
Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White;
Console.Write($"Ready to deploy {version}: "); Console.Write($"Ready to deploy {version}!");
Console.ReadLine(); pauseIfInteractive();
sw.Start(); sw.Start();
if (!string.IsNullOrEmpty(CodeSigningCertificate)) if (!string.IsNullOrEmpty(CodeSigningCertificate))
{ {
Console.Write("Enter code signing password: "); Console.Write("Enter code signing password: ");
codeSigningPassword = readLineMasked(); codeSigningPassword = args.Length > 0 ? args[0] : readLineMasked();
} }
write("Updating AssemblyInfo..."); write("Updating AssemblyInfo...");
@ -124,7 +128,7 @@ namespace osu.Desktop.Deploy
updateCsprojVersion("0.0.0"); updateCsprojVersion("0.0.0");
write("Done!", ConsoleColor.White); write("Done!", ConsoleColor.White);
Console.ReadLine(); pauseIfInteractive();
} }
private static void displayHeader() private static void displayHeader()
@ -388,10 +392,18 @@ namespace osu.Desktop.Deploy
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"FATAL ERROR: {message}"); Console.WriteLine($"FATAL ERROR: {message}");
Console.ReadLine(); pauseIfInteractive();
Environment.Exit(-1); Environment.Exit(-1);
} }
private static void pauseIfInteractive()
{
if (interactive)
Console.ReadLine();
else
Console.WriteLine();
}
private static void write(string message, ConsoleColor col = ConsoleColor.Gray) private static void write(string message, ConsoleColor col = ConsoleColor.Gray)
{ {
if (sw.ElapsedMilliseconds > 0) if (sw.ElapsedMilliseconds > 0)

View File

@ -11,7 +11,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="NuGet.CommandLine" Version="4.5.1" /> <PackageReference Include="NuGet.CommandLine" Version="4.5.1" />
<PackageReference Include="NUnit" Version="3.8.1" /> <PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="squirrel.windows" Version="1.7.8" Condition="'$(TargetFramework)' == 'net461'" /> <PackageReference Include="squirrel.windows" Version="1.7.8" Condition="'$(TargetFramework)' == 'net461'" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.0" /> <PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.0" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,62 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
using OpenTK;
namespace osu.Game.Rulesets.Catch.Tests
{
public class TestCaseAutoJuiceStream : TestCasePlayer
{
public TestCaseAutoJuiceStream()
: base(new CatchRuleset())
{
}
protected override Beatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 },
Ruleset = ruleset.RulesetInfo
}
};
for (int i = 0; i < 100; i++)
{
float width = (i % 10 + 1) / 20f;
beatmap.HitObjects.Add(new JuiceStream
{
X = 0.5f - width / 2,
ControlPoints = new List<Vector2>
{
Vector2.Zero,
new Vector2(width * CatchPlayfield.BASE_WIDTH, 0)
},
CurveType = CurveType.Linear,
Distance = width * CatchPlayfield.BASE_WIDTH,
StartTime = i * 2000,
NewCombo = i % 8 == 0
});
}
return beatmap;
}
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
{
beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
return base.CreatePlayer(beatmap, ruleset);
}
}
}

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Catch namespace osu.Game.Rulesets.Catch
{ {
@ -29,6 +30,44 @@ namespace osu.Game.Rulesets.Catch
new KeyBinding(InputKey.Shift, CatchAction.Dash), new KeyBinding(InputKey.Shift, CatchAction.Dash),
}; };
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new CatchModNightcore();
else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new CatchModDoubleTime();
if (mods.HasFlag(LegacyMods.Autoplay))
yield return new CatchModAutoplay();
if (mods.HasFlag(LegacyMods.Easy))
yield return new CatchModEasy();
if (mods.HasFlag(LegacyMods.Flashlight))
yield return new CatchModFlashlight();
if (mods.HasFlag(LegacyMods.HalfTime))
yield return new CatchModHalfTime();
if (mods.HasFlag(LegacyMods.HardRock))
yield return new CatchModHardRock();
if (mods.HasFlag(LegacyMods.Hidden))
yield return new CatchModHidden();
if (mods.HasFlag(LegacyMods.NoFail))
yield return new CatchModNoFail();
if (mods.HasFlag(LegacyMods.Perfect))
yield return new CatchModPerfect();
if (mods.HasFlag(LegacyMods.Relax))
yield return new CatchModRelax();
if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new CatchModSuddenDeath();
}
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {
switch (type) switch (type)

View File

@ -1,12 +1,88 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using System;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods
{ {
public class CatchModHardRock : ModHardRock public class CatchModHardRock : ModHardRock, IApplicableToHitObject<CatchHitObject>
{ {
public override double ScoreMultiplier => 1.12; public override double ScoreMultiplier => 1.12;
public override bool Ranked => true;
private float lastStartX;
private int lastStartTime;
public void ApplyToHitObject(CatchHitObject hitObject)
{
float position = hitObject.X;
int startTime = (int)hitObject.StartTime;
if (lastStartX == 0)
{
lastStartX = position;
lastStartTime = startTime;
return;
}
float diff = lastStartX - position;
int timeDiff = startTime - lastStartTime;
if (timeDiff > 1000)
{
lastStartX = position;
lastStartTime = startTime;
return;
}
if (diff == 0)
{
bool right = RNG.NextBool();
float rand = Math.Min(20, (float)RNG.NextDouble(0, timeDiff / 4d)) / CatchPlayfield.BASE_WIDTH;
if (right)
{
if (position + rand <= 1)
position += rand;
else
position -= rand;
}
else
{
if (position - rand >= 0)
position -= rand;
else
position += rand;
}
hitObject.X = position;
return;
}
if (Math.Abs(diff) < timeDiff / 3d)
{
if (diff > 0)
{
if (position - diff > 0)
position -= diff;
}
else
{
if (position - diff < 1)
position -= diff;
}
}
hitObject.X = position;
lastStartX = position;
lastStartTime = startTime;
}
} }
} }

View File

@ -2,11 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using osu.Framework.Testing;
// We publish our internal attributes to other sub-projects of the framework. // We publish our internal attributes to other sub-projects of the framework.
// Note, that we omit visual tests as they are meant to test the framework // Note, that we omit visual tests as they are meant to test the framework
// behavior "in the wild". // behavior "in the wild".
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests")] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests")]
[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.Dynamic")]

View File

@ -108,6 +108,7 @@ namespace osu.Game.Rulesets.Catch.Replays
case BananaShower.Banana _: case BananaShower.Banana _:
case TinyDroplet _: case TinyDroplet _:
case Droplet _: case Droplet _:
case Fruit _:
moveToNext(nestedObj); moveToNext(nestedObj);
break; break;
} }

View File

@ -30,10 +30,10 @@ namespace osu.Game.Rulesets.Catch.UI
Anchor = Anchor.TopCentre; Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;
ScaledContent.Anchor = Anchor.BottomLeft; base.Content.Anchor = Anchor.BottomLeft;
ScaledContent.Origin = Anchor.BottomLeft; base.Content.Origin = Anchor.BottomLeft;
ScaledContent.AddRange(new Drawable[] base.Content.AddRange(new Drawable[]
{ {
explodingFruitContainer = new Container explodingFruitContainer = new Container
{ {

View File

@ -14,6 +14,7 @@ using osu.Framework.Input.Bindings;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Mania namespace osu.Game.Rulesets.Mania
{ {
@ -21,6 +22,74 @@ namespace osu.Game.Rulesets.Mania
{ {
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset); public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset);
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new ManiaModNightcore();
else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new ManiaModDoubleTime();
if (mods.HasFlag(LegacyMods.Autoplay))
yield return new ManiaModAutoplay();
if (mods.HasFlag(LegacyMods.Easy))
yield return new ManiaModEasy();
if (mods.HasFlag(LegacyMods.FadeIn))
yield return new ManiaModFadeIn();
if (mods.HasFlag(LegacyMods.Flashlight))
yield return new ManiaModFlashlight();
if (mods.HasFlag(LegacyMods.HalfTime))
yield return new ManiaModHalfTime();
if (mods.HasFlag(LegacyMods.HardRock))
yield return new ManiaModHardRock();
if (mods.HasFlag(LegacyMods.Hidden))
yield return new ManiaModHidden();
if (mods.HasFlag(LegacyMods.Key1))
yield return new ManiaModKey1();
if (mods.HasFlag(LegacyMods.Key2))
yield return new ManiaModKey2();
if (mods.HasFlag(LegacyMods.Key3))
yield return new ManiaModKey3();
if (mods.HasFlag(LegacyMods.Key4))
yield return new ManiaModKey4();
if (mods.HasFlag(LegacyMods.Key5))
yield return new ManiaModKey5();
if (mods.HasFlag(LegacyMods.Key6))
yield return new ManiaModKey6();
if (mods.HasFlag(LegacyMods.Key7))
yield return new ManiaModKey7();
if (mods.HasFlag(LegacyMods.Key8))
yield return new ManiaModKey8();
if (mods.HasFlag(LegacyMods.Key9))
yield return new ManiaModKey9();
if (mods.HasFlag(LegacyMods.NoFail))
yield return new ManiaModNoFail();
if (mods.HasFlag(LegacyMods.Perfect))
yield return new ManiaModPerfect();
if (mods.HasFlag(LegacyMods.Random))
yield return new ManiaModRandom();
if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new ManiaModSuddenDeath();
}
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {
switch (type) switch (type)

View File

@ -2,11 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using osu.Framework.Testing;
// We publish our internal attributes to other sub-projects of the framework. // We publish our internal attributes to other sub-projects of the framework.
// Note, that we omit visual tests as they are meant to test the framework // Note, that we omit visual tests as they are meant to test the framework
// behavior "in the wild". // behavior "in the wild".
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests")] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests")]
[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests.Dynamic")]

View File

@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
Size = hitCircle.Size; Size = hitCircle.Size;
Scale = hitCircle.Scale; Scale = hitCircle.Scale;
CornerRadius = Size.X / 2;
AddInternal(new RingPiece()); AddInternal(new RingPiece());
hitCircle.HitObject.PositionChanged += _ => Position = hitCircle.Position; hitCircle.HitObject.PositionChanged += _ => Position = hitCircle.Position;

View File

@ -38,6 +38,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
Scale = slider.HeadCircle.Scale; Scale = slider.HeadCircle.Scale;
AddInternal(new RingPiece()); AddInternal(new RingPiece());
Select();
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -52,5 +54,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
RelativeAnchorPosition = hitObject.RelativeAnchorPosition; RelativeAnchorPosition = hitObject.RelativeAnchorPosition;
} }
// Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input.
public override bool HandleMouseInput => false;
} }
} }

View File

@ -3,6 +3,7 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
@ -59,5 +60,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
} }
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos); public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos);
public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition);
public override Quad SelectionQuad => body.PathDrawQuad;
} }
} }

View File

@ -8,5 +8,6 @@ namespace osu.Game.Rulesets.Osu.Edit
public class OsuEditPlayfield : OsuPlayfield public class OsuEditPlayfield : OsuPlayfield
{ {
protected override bool ProxyApproachCircles => false; protected override bool ProxyApproachCircles => false;
protected override bool DisplayJudgements => false;
} }
} }

View File

@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public class OsuModAutoplay : ModAutoplay<OsuHitObject> public class OsuModAutoplay : ModAutoplay<OsuHitObject>
{ {
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray(); public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray();
protected override Score CreateReplayScore(Beatmap<OsuHitObject> beatmap) protected override Score CreateReplayScore(Beatmap<OsuHitObject> beatmap)
{ {

View File

@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject> public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject>
{ {
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
public override bool Ranked => true;
public void ApplyToHitObject(OsuHitObject hitObject) public void ApplyToHitObject(OsuHitObject hitObject)
{ {

View File

@ -9,6 +9,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public class OsuModNoFail : ModNoFail public class OsuModNoFail : ModNoFail
{ {
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
} }
} }

View File

@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModRelax : ModRelax public class OsuModRelax : ModRelax
{ {
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
} }
} }

View File

@ -9,6 +9,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public class OsuModSuddenDeath : ModSuddenDeath public class OsuModSuddenDeath : ModSuddenDeath
{ {
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
} }
} }

View File

@ -10,7 +10,6 @@ using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Framework.Graphics.Primitives;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -177,8 +176,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public Drawable ProxiedLayer => HeadCircle.ApproachCircle; public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos); public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos);
public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition);
public override Quad SelectionQuad => Body.PathDrawQuad;
} }
} }

View File

@ -7,10 +7,11 @@ using osu.Game.Rulesets.Objects;
using OpenTK; using OpenTK;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit.Types;
namespace osu.Game.Rulesets.Osu.Objects namespace osu.Game.Rulesets.Osu.Objects
{ {
public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasEditablePosition
{ {
public const double OBJECT_RADIUS = 64; public const double OBJECT_RADIUS = 64;

View File

@ -21,6 +21,7 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Osu namespace osu.Game.Rulesets.Osu
{ {
@ -66,6 +67,53 @@ namespace osu.Game.Rulesets.Osu
}; };
} }
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new OsuModNightcore();
else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new OsuModDoubleTime();
if (mods.HasFlag(LegacyMods.Autopilot))
yield return new OsuModAutopilot();
if (mods.HasFlag(LegacyMods.Autoplay))
yield return new OsuModAutoplay();
if (mods.HasFlag(LegacyMods.Easy))
yield return new OsuModEasy();
if (mods.HasFlag(LegacyMods.Flashlight))
yield return new OsuModFlashlight();
if (mods.HasFlag(LegacyMods.HalfTime))
yield return new OsuModHalfTime();
if (mods.HasFlag(LegacyMods.HardRock))
yield return new OsuModHardRock();
if (mods.HasFlag(LegacyMods.Hidden))
yield return new OsuModHidden();
if (mods.HasFlag(LegacyMods.NoFail))
yield return new OsuModNoFail();
if (mods.HasFlag(LegacyMods.Perfect))
yield return new OsuModPerfect();
if (mods.HasFlag(LegacyMods.Relax))
yield return new OsuModRelax();
if (mods.HasFlag(LegacyMods.SpunOut))
yield return new OsuModSpunOut();
if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new OsuModSuddenDeath();
if (mods.HasFlag(LegacyMods.Target))
yield return new OsuModTarget();
}
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {
switch (type) switch (type)

View File

@ -2,11 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using osu.Framework.Testing;
// We publish our internal attributes to other sub-projects of the framework. // We publish our internal attributes to other sub-projects of the framework.
// Note, that we omit visual tests as they are meant to test the framework // Note, that we omit visual tests as they are meant to test the framework
// behavior "in the wild". // behavior "in the wild".
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests")] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests")]
[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests.Dynamic")]

View File

@ -92,17 +92,17 @@ namespace osu.Game.Rulesets.Osu.Replays
if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Miss), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
} }
else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
} }
else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh) > endTime + h.HitWindows.HalfWindowFor(HitResult.Meh) + 50) else if (h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good) > endTime + h.HitWindows.HalfWindowFor(HitResult.Good) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y))); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new OsuReplayFrame(endTime + h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(prev.StackedEndPosition.X, prev.StackedEndPosition.Y)));
if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Meh), new Vector2(h.StackedPosition.X, h.StackedPosition.Y))); if (!(h is Spinner)) AddFrameToReplay(new OsuReplayFrame(h.StartTime - h.HitWindows.HalfWindowFor(HitResult.Good), new Vector2(h.StackedPosition.X, h.StackedPosition.Y)));
} }
} }

View File

@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Replays
{ {
new ReplayState<OsuAction> new ReplayState<OsuAction>
{ {
Mouse = new ReplayMouseState(ToScreenSpace(Position ?? Vector2.Zero)), Mouse = new ReplayMouseState(GamefieldToScreenSpace(Position ?? Vector2.Zero)),
PressedActions = CurrentFrame.Actions PressedActions = CurrentFrame.Actions
} }
}; };

View File

@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Osu.UI
// Todo: This should not be a thing, but is currently required for the editor // Todo: This should not be a thing, but is currently required for the editor
// https://github.com/ppy/osu-framework/issues/1283 // https://github.com/ppy/osu-framework/issues/1283
protected virtual bool ProxyApproachCircles => true; protected virtual bool ProxyApproachCircles => true;
protected virtual bool DisplayJudgements => true;
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384); public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
@ -73,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.UI
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
{ {
if (!judgedObject.DisplayJudgement) if (!judgedObject.DisplayJudgement || !DisplayJudgements)
return; return;
DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject) DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject)

View File

@ -8,5 +8,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
public class TaikoModHardRock : ModHardRock public class TaikoModHardRock : ModHardRock
{ {
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
public override bool Ranked => true;
} }
} }

View File

@ -2,11 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using osu.Framework.Testing;
// We publish our internal attributes to other sub-projects of the framework. // We publish our internal attributes to other sub-projects of the framework.
// Note, that we omit visual tests as they are meant to test the framework // Note, that we omit visual tests as they are meant to test the framework
// behavior "in the wild". // behavior "in the wild".
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests")] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests")]
[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)] [assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests.Dynamic")]

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Rulesets.Taiko.Replays;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Taiko namespace osu.Game.Rulesets.Taiko
{ {
@ -31,6 +32,44 @@ namespace osu.Game.Rulesets.Taiko
new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim), new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim),
}; };
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new TaikoModNightcore();
else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new TaikoModDoubleTime();
if (mods.HasFlag(LegacyMods.Autoplay))
yield return new TaikoModAutoplay();
if (mods.HasFlag(LegacyMods.Easy))
yield return new TaikoModEasy();
if (mods.HasFlag(LegacyMods.Flashlight))
yield return new TaikoModFlashlight();
if (mods.HasFlag(LegacyMods.HalfTime))
yield return new TaikoModHalfTime();
if (mods.HasFlag(LegacyMods.HardRock))
yield return new TaikoModHardRock();
if (mods.HasFlag(LegacyMods.Hidden))
yield return new TaikoModHidden();
if (mods.HasFlag(LegacyMods.NoFail))
yield return new TaikoModNoFail();
if (mods.HasFlag(LegacyMods.Perfect))
yield return new TaikoModPerfect();
if (mods.HasFlag(LegacyMods.Relax))
yield return new TaikoModRelax();
if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new TaikoModSuddenDeath();
}
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {
switch (type) switch (type)

View File

@ -0,0 +1,62 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Overlays.Profile.Header;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseBadgeContainer : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BadgeContainer) };
public TestCaseBadgeContainer()
{
BadgeContainer badgeContainer;
Child = badgeContainer = new BadgeContainer
{
RelativeSizeAxes = Axes.Both
};
AddStep("Show 1 badge", () => badgeContainer.ShowBadges(new[]
{
new Badge
{
AwardedAt = DateTimeOffset.Now,
Description = "Appreciates compasses",
ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png",
}
}));
AddStep("Show 2 badges", () => badgeContainer.ShowBadges(new[]
{
new Badge
{
AwardedAt = DateTimeOffset.Now,
Description = "Contributed to osu!lazer testing",
ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.png",
},
new Badge
{
AwardedAt = DateTimeOffset.Now,
Description = "Appreciates compasses",
ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png",
}
}));
AddStep("Show many badges", () => badgeContainer.ShowBadges(Enumerable.Range(1, 20).Select(i => new Badge
{
AwardedAt = DateTimeOffset.Now,
Description = $"Contributed to osu!lazer testing {i} times",
ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg",
}).ToArray()));
}
}
}

View File

@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual
private readonly Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>(); private readonly Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>();
private readonly HashSet<int> eagerSelectedIDs = new HashSet<int>();
private BeatmapInfo currentSelection; private BeatmapInfo currentSelection;
@ -80,6 +81,7 @@ namespace osu.Game.Tests.Visual
testEmptyTraversal(); testEmptyTraversal();
testHiding(); testHiding();
testSelectingFilteredRuleset(); testSelectingFilteredRuleset();
testCarouselRootIsRandom();
} }
private void ensureRandomFetchSuccess() => private void ensureRandomFetchSuccess() =>
@ -151,6 +153,17 @@ namespace osu.Game.Tests.Visual
AddAssert("Selection is visible", selectedBeatmapVisible); AddAssert("Selection is visible", selectedBeatmapVisible);
} }
private void checkNonmatchingFilter()
{
AddStep("Toggle non-matching filter", () =>
{
carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false);
carousel.Filter(new FilterCriteria(), false);
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
}
);
}
/// <summary> /// <summary>
/// Test keyboard traversal /// Test keyboard traversal
/// </summary> /// </summary>
@ -403,6 +416,23 @@ namespace osu.Game.Tests.Visual
AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle)); AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle));
} }
private void testCarouselRootIsRandom()
{
List<BeatmapSetInfo> beatmapSets = new List<BeatmapSetInfo>();
for (int i = 1; i <= 50; i++)
beatmapSets.Add(createTestBeatmapSet(i));
AddStep("Load 50 Beatmaps", () => { carousel.BeatmapSets = beatmapSets; });
advanceSelection(direction: 1, diff: false);
checkNonmatchingFilter();
checkNonmatchingFilter();
checkNonmatchingFilter();
checkNonmatchingFilter();
checkNonmatchingFilter();
AddAssert("Selection was random", () => eagerSelectedIDs.Count > 1);
}
private BeatmapSetInfo createTestBeatmapSet(int id) private BeatmapSetInfo createTestBeatmapSet(int id)
{ {
return new BeatmapSetInfo return new BeatmapSetInfo

View File

@ -14,6 +14,8 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Users; using osu.Game.Users;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
@ -47,11 +49,11 @@ namespace osu.Game.Tests.Visual
AddStep("scores pack 1", () => scoresContainer.Scores = scores); AddStep("scores pack 1", () => scoresContainer.Scores = scores);
AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores); AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores);
AddStep("only top score", () => scoresContainer.Scores = new[] { topScore }); AddStep("only top score", () => scoresContainer.Scores = new[] { topScore });
AddStep("remove scores", scoresContainer.CleanAllScores); AddStep("remove scores", () => scoresContainer.Scores = null);
AddStep("turn on loading", () => scoresContainer.IsLoading = true);
AddStep("turn off loading", () => scoresContainer.IsLoading = false);
AddStep("resize to big", () => container.ResizeWidthTo(1, 300)); AddStep("resize to big", () => container.ResizeWidthTo(1, 300));
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300)); AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapSetID = 1, OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
scores = new[] scores = new[]
{ {

View File

@ -8,6 +8,9 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Overlays.BeatmapSet.Buttons;
using osu.Game.Overlays.BeatmapSet.Scores;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Users; using osu.Game.Users;
@ -18,6 +21,26 @@ namespace osu.Game.Tests.Visual
{ {
private readonly BeatmapSetOverlay overlay; private readonly BeatmapSetOverlay overlay;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Header),
typeof(ClickableUsername),
typeof(DrawableScore),
typeof(DrawableTopScore),
typeof(ScoresContainer),
typeof(AuthorInfo),
typeof(BasicStats),
typeof(BeatmapPicker),
typeof(Details),
typeof(DownloadButton),
typeof(FavouriteButton),
typeof(Header),
typeof(HeaderButton),
typeof(Info),
typeof(PreviewButton),
typeof(SuccessRate),
};
public TestCaseBeatmapSetOverlay() public TestCaseBeatmapSetOverlay()
{ {
Add(overlay = new BeatmapSetOverlay()); Add(overlay = new BeatmapSetOverlay());
@ -29,6 +52,10 @@ namespace osu.Game.Tests.Visual
var mania = rulesets.GetRuleset(3); var mania = rulesets.GetRuleset(3);
var taiko = rulesets.GetRuleset(1); var taiko = rulesets.GetRuleset(1);
AddStep(@"show loading", () => overlay.ShowBeatmapSet(null));
AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55));
AddStep(@"show first", () => AddStep(@"show first", () =>
{ {
overlay.ShowBeatmapSet(new BeatmapSetInfo overlay.ShowBeatmapSet(new BeatmapSetInfo
@ -52,6 +79,7 @@ namespace osu.Game.Tests.Visual
FavouriteCount = 356, FavouriteCount = 356,
Submitted = new DateTime(2016, 2, 10), Submitted = new DateTime(2016, 2, 10),
Ranked = new DateTime(2016, 6, 19), Ranked = new DateTime(2016, 6, 19),
Status = BeatmapSetOnlineStatus.Ranked,
BPM = 236, BPM = 236,
HasVideo = true, HasVideo = true,
Covers = new BeatmapSetOnlineCovers Covers = new BeatmapSetOnlineCovers
@ -222,6 +250,7 @@ namespace osu.Game.Tests.Visual
FavouriteCount = 58, FavouriteCount = 58,
Submitted = new DateTime(2016, 6, 11), Submitted = new DateTime(2016, 6, 11),
Ranked = new DateTime(2016, 7, 12), Ranked = new DateTime(2016, 7, 12),
Status = BeatmapSetOnlineStatus.Pending,
BPM = 160, BPM = 160,
HasVideo = false, HasVideo = false,
Covers = new BeatmapSetOnlineCovers Covers = new BeatmapSetOnlineCovers

View File

@ -2,7 +2,9 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
@ -10,10 +12,12 @@ namespace osu.Game.Tests.Visual
[TestFixture] [TestFixture]
public class TestCaseBreadcrumbs : OsuTestCase public class TestCaseBreadcrumbs : OsuTestCase
{ {
private readonly BreadcrumbControl<BreadcrumbTab> breadcrumbs;
public TestCaseBreadcrumbs() public TestCaseBreadcrumbs()
{ {
BreadcrumbControl<BreadcrumbTab> c;
Add(c = new BreadcrumbControl<BreadcrumbTab> Add(breadcrumbs = new BreadcrumbControl<BreadcrumbTab>
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -21,9 +25,15 @@ namespace osu.Game.Tests.Visual
Width = 0.5f, Width = 0.5f,
}); });
AddStep(@"first", () => c.Current.Value = BreadcrumbTab.Click); AddStep(@"first", () => breadcrumbs.Current.Value = BreadcrumbTab.Click);
AddStep(@"second", () => c.Current.Value = BreadcrumbTab.The); AddStep(@"second", () => breadcrumbs.Current.Value = BreadcrumbTab.The);
AddStep(@"third", () => c.Current.Value = BreadcrumbTab.Circles); AddStep(@"third", () => breadcrumbs.Current.Value = BreadcrumbTab.Circles);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
breadcrumbs.StripColour = colours.Blue;
} }
private enum BreadcrumbTab private enum BreadcrumbTab

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
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;
using OpenTK; using OpenTK;
@ -18,15 +17,12 @@ using OpenTK.Graphics;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
[TestFixture] [TestFixture]
public class TestCaseCursors : OsuTestCase public class TestCaseCursors : ManualInputManagerTestCase
{ {
private readonly ManualInputManager inputManager;
private readonly CursorOverrideContainer cursorOverrideContainer; private readonly CursorOverrideContainer cursorOverrideContainer;
private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6]; private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6];
public TestCaseCursors() public TestCaseCursors()
{
Child = inputManager = new ManualInputManager
{ {
Child = cursorOverrideContainer = new CursorOverrideContainer Child = cursorOverrideContainer = new CursorOverrideContainer
{ {
@ -80,26 +76,15 @@ namespace osu.Game.Tests.Visual
Size = new Vector2(0.2f, 1), Size = new Vector2(0.2f, 1),
}, },
} }
}
}; };
returnUserInput();
AddToggleStep("Smooth transitions", b => cursorBoxes.ForEach(box => box.SmoothTransition = b)); AddToggleStep("Smooth transitions", b => cursorBoxes.ForEach(box => box.SmoothTransition = b));
testUserCursor(); testUserCursor();
testLocalCursor(); testLocalCursor();
testUserCursorOverride(); testUserCursorOverride();
testMultipleLocalCursors(); testMultipleLocalCursors();
returnUserInput(); ReturnUserInput();
}
/// <summary>
/// Returns input back to the user.
/// </summary>
private void returnUserInput()
{
AddStep("Return user input", () => inputManager.UseParentState = true);
} }
/// <summary> /// <summary>
@ -109,7 +94,7 @@ namespace osu.Game.Tests.Visual
/// </summary> /// </summary>
private void testUserCursor() private void testUserCursor()
{ {
AddStep("Move to green area", () => inputManager.MoveMouseTo(cursorBoxes[0])); AddStep("Move to green area", () => InputManager.MoveMouseTo(cursorBoxes[0]));
AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check green cursor visible", () => checkVisible(cursorBoxes[0].Cursor));
AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor)); AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor));
AddStep("Move out", moveOut); AddStep("Move out", moveOut);
@ -124,7 +109,7 @@ namespace osu.Game.Tests.Visual
/// </summary> /// </summary>
private void testLocalCursor() private void testLocalCursor()
{ {
AddStep("Move to purple area", () => inputManager.MoveMouseTo(cursorBoxes[3])); AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3]));
AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor));
AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor));
AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor));
@ -141,7 +126,7 @@ namespace osu.Game.Tests.Visual
/// </summary> /// </summary>
private void testUserCursorOverride() private void testUserCursorOverride()
{ {
AddStep("Move to blue-green boundary", () => inputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); AddStep("Move to blue-green boundary", () => InputManager.MoveMouseTo(cursorBoxes[1].ScreenSpaceDrawQuad.BottomRight - new Vector2(10)));
AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor));
AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor));
AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor));
@ -156,7 +141,7 @@ namespace osu.Game.Tests.Visual
/// </summary> /// </summary>
private void testMultipleLocalCursors() private void testMultipleLocalCursors()
{ {
AddStep("Move to yellow-purple boundary", () => inputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10))); AddStep("Move to yellow-purple boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.BottomRight - new Vector2(10)));
AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor));
AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor));
AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor));
@ -172,7 +157,7 @@ namespace osu.Game.Tests.Visual
/// </summary> /// </summary>
private void testUserOverrideWithLocal() private void testUserOverrideWithLocal()
{ {
AddStep("Move to yellow-blue boundary", () => inputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10))); AddStep("Move to yellow-blue boundary", () => InputManager.MoveMouseTo(cursorBoxes[5].ScreenSpaceDrawQuad.TopRight - new Vector2(10)));
AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor)); AddAssert("Check blue cursor visible", () => checkVisible(cursorBoxes[1].Cursor));
AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor)); AddAssert("Check blue cursor at mouse", () => checkAtMouse(cursorBoxes[1].Cursor));
AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor)); AddAssert("Check yellow cursor visible", () => checkVisible(cursorBoxes[5].Cursor));
@ -186,7 +171,7 @@ namespace osu.Game.Tests.Visual
/// Moves the cursor to a point not covered by any cursor containers. /// Moves the cursor to a point not covered by any cursor containers.
/// </summary> /// </summary>
private void moveOut() private void moveOut()
=> inputManager.MoveMouseTo(new Vector2(inputManager.ScreenSpaceDrawQuad.Centre.X, inputManager.ScreenSpaceDrawQuad.TopLeft.Y)); => InputManager.MoveMouseTo(new Vector2(InputManager.ScreenSpaceDrawQuad.Centre.X, InputManager.ScreenSpaceDrawQuad.TopLeft.Y));
/// <summary> /// <summary>
/// Checks if a cursor is visible. /// Checks if a cursor is visible.
@ -199,7 +184,7 @@ namespace osu.Game.Tests.Visual
/// </summary> /// </summary>
/// <param name="cursorContainer">The cursor to check.</param> /// <param name="cursorContainer">The cursor to check.</param>
private bool checkAtMouse(CursorContainer cursorContainer) private bool checkAtMouse(CursorContainer cursorContainer)
=> Precision.AlmostEquals(inputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition)); => Precision.AlmostEquals(InputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition));
private class CustomCursorBox : Container, IProvideCursor private class CustomCursorBox : Container, IProvideCursor
{ {

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Timing;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Screens.Compose; using osu.Game.Screens.Edit.Screens.Compose;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
@ -13,24 +12,15 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
[TestFixture] [TestFixture]
public class TestCaseEditorCompose : OsuTestCase public class TestCaseEditorCompose : EditorClockTestCase
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(Compose) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(Compose) };
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(parent);
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuGameBase osuGame) private void load(OsuGameBase osuGame)
{ {
osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
dependencies.CacheAs<IAdjustableClock>(clock);
dependencies.CacheAs<IFrameBasedClock>(clock);
var compose = new Compose(); var compose = new Compose();
compose.Beatmap.BindTo(osuGame.Beatmap); compose.Beatmap.BindTo(osuGame.Beatmap);

View File

@ -3,52 +3,35 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
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.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Screens.Compose;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
public class TestCaseEditorSeekSnapping : OsuTestCase public class TestCaseEditorSeekSnapping : EditorClockTestCase
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(HitObjectComposer) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(HitObjectComposer) };
private Track track; public TestCaseEditorSeekSnapping()
private HitObjectComposer composer; {
BeatDivisor.Value = 4;
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(4); }
private DecoupleableInterpolatingFramedClock clock;
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(parent);
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuGameBase osuGame) private void load(OsuGameBase osuGame)
{ {
clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
dependencies.CacheAs<IAdjustableClock>(clock);
dependencies.CacheAs<IFrameBasedClock>(clock);
dependencies.Cache(beatDivisor);
var testBeatmap = new Beatmap var testBeatmap = new Beatmap
{ {
ControlPointInfo = new ControlPointInfo ControlPointInfo = new ControlPointInfo
@ -71,22 +54,8 @@ namespace osu.Game.Tests.Visual
}; };
osuGame.Beatmap.Value = new TestWorkingBeatmap(testBeatmap); osuGame.Beatmap.Value = new TestWorkingBeatmap(testBeatmap);
track = osuGame.Beatmap.Value.Track;
Child = new GridContainer Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock };
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[] { composer = new TestHitObjectComposer(new OsuRuleset()) },
new Drawable[] { new TimingPointVisualiser(testBeatmap, track) { Clock = clock } },
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.Distributed),
new Dimension(GridSizeMode.AutoSize),
}
};
testSeekNoSnapping(); testSeekNoSnapping();
testSeekSnappingOnBeat(); testSeekSnappingOnBeat();
@ -107,18 +76,18 @@ namespace osu.Game.Tests.Visual
reset(); reset();
// Forwards // Forwards
AddStep("Seek(0)", () => composer.SeekTo(0)); AddStep("Seek(0)", () => Clock.Seek(0));
AddAssert("Time = 0", () => clock.CurrentTime == 0); AddAssert("Time = 0", () => Clock.CurrentTime == 0);
AddStep("Seek(33)", () => composer.SeekTo(33)); AddStep("Seek(33)", () => Clock.Seek(33));
AddAssert("Time = 33", () => clock.CurrentTime == 33); AddAssert("Time = 33", () => Clock.CurrentTime == 33);
AddStep("Seek(89)", () => composer.SeekTo(89)); AddStep("Seek(89)", () => Clock.Seek(89));
AddAssert("Time = 89", () => clock.CurrentTime == 89); AddAssert("Time = 89", () => Clock.CurrentTime == 89);
// Backwards // Backwards
AddStep("Seek(25)", () => composer.SeekTo(25)); AddStep("Seek(25)", () => Clock.Seek(25));
AddAssert("Time = 25", () => clock.CurrentTime == 25); AddAssert("Time = 25", () => Clock.CurrentTime == 25);
AddStep("Seek(0)", () => composer.SeekTo(0)); AddStep("Seek(0)", () => Clock.Seek(0));
AddAssert("Time = 0", () => clock.CurrentTime == 0); AddAssert("Time = 0", () => Clock.CurrentTime == 0);
} }
/// <summary> /// <summary>
@ -129,20 +98,20 @@ namespace osu.Game.Tests.Visual
{ {
reset(); reset();
AddStep("Seek(0), Snap", () => composer.SeekTo(0, true)); AddStep("Seek(0), Snap", () => Clock.SeekSnapped(0));
AddAssert("Time = 0", () => clock.CurrentTime == 0); AddAssert("Time = 0", () => Clock.CurrentTime == 0);
AddStep("Seek(50), Snap", () => composer.SeekTo(50, true)); AddStep("Seek(50), Snap", () => Clock.SeekSnapped(50));
AddAssert("Time = 50", () => clock.CurrentTime == 50); AddAssert("Time = 50", () => Clock.CurrentTime == 50);
AddStep("Seek(100), Snap", () => composer.SeekTo(100, true)); AddStep("Seek(100), Snap", () => Clock.SeekSnapped(100));
AddAssert("Time = 100", () => clock.CurrentTime == 100); AddAssert("Time = 100", () => Clock.CurrentTime == 100);
AddStep("Seek(175), Snap", () => composer.SeekTo(175, true)); AddStep("Seek(175), Snap", () => Clock.SeekSnapped(175));
AddAssert("Time = 175", () => clock.CurrentTime == 175); AddAssert("Time = 175", () => Clock.CurrentTime == 175);
AddStep("Seek(350), Snap", () => composer.SeekTo(350, true)); AddStep("Seek(350), Snap", () => Clock.SeekSnapped(350));
AddAssert("Time = 350", () => clock.CurrentTime == 350); AddAssert("Time = 350", () => Clock.CurrentTime == 350);
AddStep("Seek(400), Snap", () => composer.SeekTo(400, true)); AddStep("Seek(400), Snap", () => Clock.SeekSnapped(400));
AddAssert("Time = 400", () => clock.CurrentTime == 400); AddAssert("Time = 400", () => Clock.CurrentTime == 400);
AddStep("Seek(450), Snap", () => composer.SeekTo(450, true)); AddStep("Seek(450), Snap", () => Clock.SeekSnapped(450));
AddAssert("Time = 450", () => clock.CurrentTime == 450); AddAssert("Time = 450", () => Clock.CurrentTime == 450);
} }
/// <summary> /// <summary>
@ -154,18 +123,18 @@ namespace osu.Game.Tests.Visual
{ {
reset(); reset();
AddStep("Seek(24), Snap", () => composer.SeekTo(24, true)); AddStep("Seek(24), Snap", () => Clock.SeekSnapped(24));
AddAssert("Time = 0", () => clock.CurrentTime == 0); AddAssert("Time = 0", () => Clock.CurrentTime == 0);
AddStep("Seek(26), Snap", () => composer.SeekTo(26, true)); AddStep("Seek(26), Snap", () => Clock.SeekSnapped(26));
AddAssert("Time = 50", () => clock.CurrentTime == 50); AddAssert("Time = 50", () => Clock.CurrentTime == 50);
AddStep("Seek(150), Snap", () => composer.SeekTo(150, true)); AddStep("Seek(150), Snap", () => Clock.SeekSnapped(150));
AddAssert("Time = 100", () => clock.CurrentTime == 100); AddAssert("Time = 100", () => Clock.CurrentTime == 100);
AddStep("Seek(170), Snap", () => composer.SeekTo(170, true)); AddStep("Seek(170), Snap", () => Clock.SeekSnapped(170));
AddAssert("Time = 175", () => clock.CurrentTime == 175); AddAssert("Time = 175", () => Clock.CurrentTime == 175);
AddStep("Seek(274), Snap", () => composer.SeekTo(274, true)); AddStep("Seek(274), Snap", () => Clock.SeekSnapped(274));
AddAssert("Time = 175", () => clock.CurrentTime == 175); AddAssert("Time = 175", () => Clock.CurrentTime == 175);
AddStep("Seek(276), Snap", () => composer.SeekTo(276, true)); AddStep("Seek(276), Snap", () => Clock.SeekSnapped(276));
AddAssert("Time = 350", () => clock.CurrentTime == 350); AddAssert("Time = 350", () => Clock.CurrentTime == 350);
} }
/// <summary> /// <summary>
@ -175,16 +144,16 @@ namespace osu.Game.Tests.Visual
{ {
reset(); reset();
AddStep("SeekForward", () => composer.SeekForward()); AddStep("SeekForward", () => Clock.SeekForward());
AddAssert("Time = 50", () => clock.CurrentTime == 50); AddAssert("Time = 50", () => Clock.CurrentTime == 50);
AddStep("SeekForward", () => composer.SeekForward()); AddStep("SeekForward", () => Clock.SeekForward());
AddAssert("Time = 100", () => clock.CurrentTime == 100); AddAssert("Time = 100", () => Clock.CurrentTime == 100);
AddStep("SeekForward", () => composer.SeekForward()); AddStep("SeekForward", () => Clock.SeekForward());
AddAssert("Time = 200", () => clock.CurrentTime == 200); AddAssert("Time = 200", () => Clock.CurrentTime == 200);
AddStep("SeekForward", () => composer.SeekForward()); AddStep("SeekForward", () => Clock.SeekForward());
AddAssert("Time = 400", () => clock.CurrentTime == 400); AddAssert("Time = 400", () => Clock.CurrentTime == 400);
AddStep("SeekForward", () => composer.SeekForward()); AddStep("SeekForward", () => Clock.SeekForward());
AddAssert("Time = 450", () => clock.CurrentTime == 450); AddAssert("Time = 450", () => Clock.CurrentTime == 450);
} }
/// <summary> /// <summary>
@ -194,18 +163,18 @@ namespace osu.Game.Tests.Visual
{ {
reset(); reset();
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 50", () => clock.CurrentTime == 50); AddAssert("Time = 50", () => Clock.CurrentTime == 50);
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 100", () => clock.CurrentTime == 100); AddAssert("Time = 100", () => Clock.CurrentTime == 100);
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 175", () => clock.CurrentTime == 175); AddAssert("Time = 175", () => Clock.CurrentTime == 175);
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 350", () => clock.CurrentTime == 350); AddAssert("Time = 350", () => Clock.CurrentTime == 350);
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 400", () => clock.CurrentTime == 400); AddAssert("Time = 400", () => Clock.CurrentTime == 400);
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 450", () => clock.CurrentTime == 450); AddAssert("Time = 450", () => Clock.CurrentTime == 450);
} }
/// <summary> /// <summary>
@ -216,30 +185,30 @@ namespace osu.Game.Tests.Visual
{ {
reset(); reset();
AddStep("Seek(49)", () => composer.SeekTo(49)); AddStep("Seek(49)", () => Clock.Seek(49));
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 50", () => clock.CurrentTime == 50); AddAssert("Time = 50", () => Clock.CurrentTime == 50);
AddStep("Seek(49.999)", () => composer.SeekTo(49.999)); AddStep("Seek(49.999)", () => Clock.Seek(49.999));
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 50", () => clock.CurrentTime == 50); AddAssert("Time = 50", () => Clock.CurrentTime == 50);
AddStep("Seek(99)", () => composer.SeekTo(99)); AddStep("Seek(99)", () => Clock.Seek(99));
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 100", () => clock.CurrentTime == 100); AddAssert("Time = 100", () => Clock.CurrentTime == 100);
AddStep("Seek(99.999)", () => composer.SeekTo(99.999)); AddStep("Seek(99.999)", () => Clock.Seek(99.999));
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 100", () => clock.CurrentTime == 100); AddAssert("Time = 100", () => Clock.CurrentTime == 100);
AddStep("Seek(174)", () => composer.SeekTo(174)); AddStep("Seek(174)", () => Clock.Seek(174));
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 175", () => clock.CurrentTime == 175); AddAssert("Time = 175", () => Clock.CurrentTime == 175);
AddStep("Seek(349)", () => composer.SeekTo(349)); AddStep("Seek(349)", () => Clock.Seek(349));
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 350", () => clock.CurrentTime == 350); AddAssert("Time = 350", () => Clock.CurrentTime == 350);
AddStep("Seek(399)", () => composer.SeekTo(399)); AddStep("Seek(399)", () => Clock.Seek(399));
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 400", () => clock.CurrentTime == 400); AddAssert("Time = 400", () => Clock.CurrentTime == 400);
AddStep("Seek(449)", () => composer.SeekTo(449)); AddStep("Seek(449)", () => Clock.Seek(449));
AddStep("SeekForward, Snap", () => composer.SeekForward(true)); AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
AddAssert("Time = 450", () => clock.CurrentTime == 450); AddAssert("Time = 450", () => Clock.CurrentTime == 450);
} }
/// <summary> /// <summary>
@ -249,19 +218,19 @@ namespace osu.Game.Tests.Visual
{ {
reset(); reset();
AddStep("Seek(450)", () => composer.SeekTo(450)); AddStep("Seek(450)", () => Clock.Seek(450));
AddStep("SeekBackward", () => composer.SeekBackward()); AddStep("SeekBackward", () => Clock.SeekBackward());
AddAssert("Time = 425", () => clock.CurrentTime == 425); AddAssert("Time = 425", () => Clock.CurrentTime == 425);
AddStep("SeekBackward", () => composer.SeekBackward()); AddStep("SeekBackward", () => Clock.SeekBackward());
AddAssert("Time = 375", () => clock.CurrentTime == 375); AddAssert("Time = 375", () => Clock.CurrentTime == 375);
AddStep("SeekBackward", () => composer.SeekBackward()); AddStep("SeekBackward", () => Clock.SeekBackward());
AddAssert("Time = 325", () => clock.CurrentTime == 325); AddAssert("Time = 325", () => Clock.CurrentTime == 325);
AddStep("SeekBackward", () => composer.SeekBackward()); AddStep("SeekBackward", () => Clock.SeekBackward());
AddAssert("Time = 125", () => clock.CurrentTime == 125); AddAssert("Time = 125", () => Clock.CurrentTime == 125);
AddStep("SeekBackward", () => composer.SeekBackward()); AddStep("SeekBackward", () => Clock.SeekBackward());
AddAssert("Time = 25", () => clock.CurrentTime == 25); AddAssert("Time = 25", () => Clock.CurrentTime == 25);
AddStep("SeekBackward", () => composer.SeekBackward()); AddStep("SeekBackward", () => Clock.SeekBackward());
AddAssert("Time = 0", () => clock.CurrentTime == 0); AddAssert("Time = 0", () => Clock.CurrentTime == 0);
} }
/// <summary> /// <summary>
@ -271,19 +240,19 @@ namespace osu.Game.Tests.Visual
{ {
reset(); reset();
AddStep("Seek(450)", () => composer.SeekTo(450)); AddStep("Seek(450)", () => Clock.Seek(450));
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
AddAssert("Time = 400", () => clock.CurrentTime == 400); AddAssert("Time = 400", () => Clock.CurrentTime == 400);
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
AddAssert("Time = 350", () => clock.CurrentTime == 350); AddAssert("Time = 350", () => Clock.CurrentTime == 350);
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
AddAssert("Time = 175", () => clock.CurrentTime == 175); AddAssert("Time = 175", () => Clock.CurrentTime == 175);
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
AddAssert("Time = 100", () => clock.CurrentTime == 100); AddAssert("Time = 100", () => Clock.CurrentTime == 100);
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
AddAssert("Time = 50", () => clock.CurrentTime == 50); AddAssert("Time = 50", () => Clock.CurrentTime == 50);
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
AddAssert("Time = 0", () => clock.CurrentTime == 0); AddAssert("Time = 0", () => Clock.CurrentTime == 0);
} }
/// <summary> /// <summary>
@ -294,18 +263,18 @@ namespace osu.Game.Tests.Visual
{ {
reset(); reset();
AddStep("Seek(451)", () => composer.SeekTo(451)); AddStep("Seek(451)", () => Clock.Seek(451));
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
AddAssert("Time = 450", () => clock.CurrentTime == 450); AddAssert("Time = 450", () => Clock.CurrentTime == 450);
AddStep("Seek(450.999)", () => composer.SeekTo(450.999)); AddStep("Seek(450.999)", () => Clock.Seek(450.999));
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
AddAssert("Time = 450", () => clock.CurrentTime == 450); AddAssert("Time = 450", () => Clock.CurrentTime == 450);
AddStep("Seek(401)", () => composer.SeekTo(401)); AddStep("Seek(401)", () => Clock.Seek(401));
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
AddAssert("Time = 400", () => clock.CurrentTime == 400); AddAssert("Time = 400", () => Clock.CurrentTime == 400);
AddStep("Seek(401.999)", () => composer.SeekTo(401.999)); AddStep("Seek(401.999)", () => Clock.Seek(401.999));
AddStep("SeekBackward, Snap", () => composer.SeekBackward(true)); AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
AddAssert("Time = 400", () => clock.CurrentTime == 400); AddAssert("Time = 400", () => Clock.CurrentTime == 400);
} }
/// <summary> /// <summary>
@ -317,34 +286,34 @@ namespace osu.Game.Tests.Visual
double lastTime = 0; double lastTime = 0;
AddStep("Seek(0)", () => composer.SeekTo(0)); AddStep("Seek(0)", () => Clock.Seek(0));
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
{ {
AddStep("SeekForward, Snap", () => AddStep("SeekForward, Snap", () =>
{ {
lastTime = clock.CurrentTime; lastTime = Clock.CurrentTime;
composer.SeekForward(true); Clock.SeekForward(true);
}); });
AddAssert("Time > lastTime", () => clock.CurrentTime > lastTime); AddAssert("Time > lastTime", () => Clock.CurrentTime > lastTime);
} }
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
{ {
AddStep("SeekBackward, Snap", () => AddStep("SeekBackward, Snap", () =>
{ {
lastTime = clock.CurrentTime; lastTime = Clock.CurrentTime;
composer.SeekBackward(true); Clock.SeekBackward(true);
}); });
AddAssert("Time < lastTime", () => clock.CurrentTime < lastTime); AddAssert("Time < lastTime", () => Clock.CurrentTime < lastTime);
} }
AddAssert("Time = 0", () => clock.CurrentTime == 0); AddAssert("Time = 0", () => Clock.CurrentTime == 0);
} }
private void reset() private void reset()
{ {
AddStep("Reset", () => composer.SeekTo(0)); AddStep("Reset", () => Clock.Seek(0));
} }
private class TestHitObjectComposer : HitObjectComposer private class TestHitObjectComposer : HitObjectComposer
@ -359,13 +328,13 @@ namespace osu.Game.Tests.Visual
private class TimingPointVisualiser : CompositeDrawable private class TimingPointVisualiser : CompositeDrawable
{ {
private readonly Track track; private readonly double length;
private readonly Drawable tracker; private readonly Drawable tracker;
public TimingPointVisualiser(Beatmap beatmap, Track track) public TimingPointVisualiser(Beatmap beatmap, double length)
{ {
this.track = track; this.length = length;
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;
@ -417,7 +386,7 @@ namespace osu.Game.Tests.Visual
for (int i = 0; i < timingPoints.Count; i++) for (int i = 0; i < timingPoints.Count; i++)
{ {
TimingControlPoint next = i == timingPoints.Count - 1 ? null : timingPoints[i + 1]; TimingControlPoint next = i == timingPoints.Count - 1 ? null : timingPoints[i + 1];
timelineContainer.Add(new TimingPointTimeline(timingPoints[i], next?.Time ?? beatmap.HitObjects.Last().StartTime, track.Length)); timelineContainer.Add(new TimingPointTimeline(timingPoints[i], next?.Time ?? length, length));
} }
} }
@ -425,7 +394,7 @@ namespace osu.Game.Tests.Visual
{ {
base.Update(); base.Update();
tracker.X = (float)(Time.Current / track.Length); tracker.X = (float)(Time.Current / length);
} }
private class TimingPointTimeline : CompositeDrawable private class TimingPointTimeline : CompositeDrawable

View File

@ -6,36 +6,22 @@ using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using OpenTK; using OpenTK;
using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Framework.Configuration;
using osu.Framework.Timing;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
[TestFixture] [TestFixture]
public class TestCaseEditorSummaryTimeline : OsuTestCase public class TestCaseEditorSummaryTimeline : EditorClockTestCase
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(SummaryTimeline) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(SummaryTimeline) };
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(parent);
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load(OsuGameBase osuGame)
{ {
beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo); osuGame.Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
dependencies.CacheAs<IAdjustableClock>(clock);
dependencies.CacheAs<IFrameBasedClock>(clock);
SummaryTimeline summaryTimeline; SummaryTimeline summaryTimeline;
Add(summaryTimeline = new SummaryTimeline Add(summaryTimeline = new SummaryTimeline
@ -45,7 +31,7 @@ namespace osu.Game.Tests.Visual
Size = new Vector2(500, 50) Size = new Vector2(500, 50)
}); });
summaryTimeline.Beatmap.BindTo(beatmap); summaryTimeline.Beatmap.BindTo(osuGame.Beatmap);
} }
} }
} }

View File

@ -1,90 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseGamefield : OsuTestCase
{
protected override void LoadComplete()
{
base.LoadComplete();
/*int time = 500;
for (int i = 0; i < 100; i++)
{
objects.Add(new HitCircle
{
StartTime = time,
Position = new Vector2(RNG.Next(0, (int)OsuPlayfield.BASE_SIZE.X), RNG.Next(0, (int)OsuPlayfield.BASE_SIZE.Y)),
Scale = RNG.NextSingle(0.5f, 1.0f),
});
time += RNG.Next(50, 500);
}*/
var controlPointInfo = new ControlPointInfo();
controlPointInfo.TimingPoints.Add(new TimingControlPoint
{
BeatLength = 200
});
/*WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
{
HitObjects = objects,
BeatmapInfo = new BeatmapInfo
{
Difficulty = new BeatmapDifficulty(),
Ruleset = rulesets.Query<RulesetInfo>().First(),
Metadata = new BeatmapMetadata
{
Artist = @"Unknown",
Title = @"Sample Beatmap",
Author = @"peppy",
},
},
ControlPointInfo = controlPointInfo
});
AddRange(new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
//ensure we are at offset 0
Clock = new FramedClock(),
Children = new Drawable[]
{
new OsuRulesetContainer(new OsuRuleset(), beatmap, false)
{
Scale = new Vector2(0.5f),
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft
},
new TaikoRulesetContainer(new TaikoRuleset(),beatmap, false)
{
Scale = new Vector2(0.5f),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight
},
new CatchRulesetContainer(new CatchRuleset(),beatmap, false)
{
Scale = new Vector2(0.5f),
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft
},
new ManiaRulesetContainer(new ManiaRuleset(),beatmap, false)
{
Scale = new Vector2(0.5f),
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight
}
}
}
});*/
}
}
}

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using JetBrains.Annotations;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Timing; using osu.Framework.Timing;
@ -12,7 +13,6 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Screens.Compose.Layers; using osu.Game.Screens.Edit.Screens.Compose.Layers;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
@ -20,19 +20,16 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
[TestFixture] [TestFixture]
public class TestCaseEditorSelectionLayer : OsuTestCase public class TestCaseHitObjectComposer : OsuTestCase
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
typeof(SelectionLayer), typeof(MaskSelection),
typeof(SelectionBox), typeof(DragLayer),
typeof(HitObjectComposer), typeof(HitObjectComposer),
typeof(OsuHitObjectComposer), typeof(OsuHitObjectComposer),
typeof(HitObjectMaskLayer), typeof(HitObjectMaskLayer),
typeof(HitObjectMask), typeof(NotNullAttribute)
typeof(HitCircleMask),
typeof(SliderMask),
typeof(SliderCircleMask)
}; };
private DependencyContainer dependencies; private DependencyContainer dependencies;

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Overlays.Profile;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using OpenTK; using OpenTK;
@ -11,6 +10,7 @@ using System.Collections.Generic;
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Profile.Header;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual

View File

@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual
Direction = direction; Direction = direction;
Padding = new MarginPadding(2); Padding = new MarginPadding(2);
ScaledContent.Masking = true; Content.Masking = true;
AddInternal(new Box AddInternal(new Box
{ {

View File

@ -8,6 +8,7 @@ using NUnit.Framework;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile;
using osu.Game.Overlays.Profile.Header;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
@ -23,6 +24,7 @@ namespace osu.Game.Tests.Visual
typeof(UserProfileOverlay), typeof(UserProfileOverlay),
typeof(RankGraph), typeof(RankGraph),
typeof(LineGraph), typeof(LineGraph),
typeof(BadgeContainer)
}; };
public TestCaseUserProfile() public TestCaseUserProfile()
@ -53,6 +55,15 @@ namespace osu.Game.Tests.Visual
{ {
Mode = @"osu", Mode = @"osu",
Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
},
Badges = new[]
{
new Badge
{
AwardedAt = DateTimeOffset.FromUnixTimeSeconds(1505741569),
Description = "Outstanding help by being a voluntary test subject.",
ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg"
}
} }
}, false)); }, false));

View File

@ -0,0 +1,54 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseWaveContainer : OsuTestCase
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
WaveContainer container;
Add(container = new WaveContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(400),
FirstWaveColour = colours.Red,
SecondWaveColour = colours.Green,
ThirdWaveColour = colours.Blue,
FourthWaveColour = colours.Pink,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 20,
Text = @"Wave Container",
},
},
});
AddStep(@"show", container.Show);
AddStep(@"hide", container.Hide);
}
}
}

View File

@ -3,6 +3,9 @@
<PropertyGroup Label="C#"> <PropertyGroup Label="C#">
<LangVersion>7</LangVersion> <LangVersion>7</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<ApplicationManifest>..\app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup Label="License"> <ItemGroup Label="License">
<None Include="..\osu.licenseheader"> <None Include="..\osu.licenseheader">
<Link>osu.licenseheader</Link> <Link>osu.licenseheader</Link>

View File

@ -26,6 +26,11 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
public DateTimeOffset? LastUpdated { get; set; } public DateTimeOffset? LastUpdated { get; set; }
/// <summary>
/// The status of this beatmap set.
/// </summary>
public BeatmapSetOnlineStatus Status { get; set; }
/// <summary> /// <summary>
/// Whether or not this beatmap set has a background video. /// Whether or not this beatmap set has a background video.
/// </summary> /// </summary>

View File

@ -0,0 +1,17 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Beatmaps
{
public enum BeatmapSetOnlineStatus
{
None = -3,
Graveyard = -2,
WIP = -1,
Pending = 0,
Ranked = 1,
Approved = 2,
Qualified = 3,
Loved = 4,
}
}

View File

@ -0,0 +1,54 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
public class BeatmapSetOnlineStatusPill : CircularContainer
{
private readonly OsuSpriteText statusText;
private BeatmapSetOnlineStatus status = BeatmapSetOnlineStatus.None;
public BeatmapSetOnlineStatus Status
{
get { return status; }
set
{
if (value == status) return;
status = value;
statusText.Text = Enum.GetName(typeof(BeatmapSetOnlineStatus), Status)?.ToUpper();
}
}
public BeatmapSetOnlineStatusPill(float textSize, MarginPadding textPadding)
{
AutoSizeAxes = Axes.Both;
Masking = true;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
statusText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = @"Exo2.0-Bold",
TextSize = textSize,
Padding = textPadding,
},
};
}
}
}

View File

@ -16,7 +16,7 @@ namespace osu.Game.Beatmaps.Formats
public TOutput Decode(StreamReader primaryStream, params StreamReader[] otherStreams) public TOutput Decode(StreamReader primaryStream, params StreamReader[] otherStreams)
{ {
var output = CreateTemplateObject(); var output = CreateTemplateObject();
foreach (StreamReader stream in new[] { primaryStream }.Concat(otherStreams)) foreach (StreamReader stream in otherStreams.Prepend(primaryStream))
ParseStreamInto(stream, output); ParseStreamInto(stream, output);
return output; return output;
} }

View File

@ -81,13 +81,13 @@ namespace osu.Game.Beatmaps.Formats
handleDifficulty(line); handleDifficulty(line);
return; return;
case Section.Events: case Section.Events:
handleEvents(line); handleEvent(line);
return; return;
case Section.TimingPoints: case Section.TimingPoints:
handleTimingPoints(line); handleTimingPoint(line);
return; return;
case Section.HitObjects: case Section.HitObjects:
handleHitObjects(line); handleHitObject(line);
return; return;
} }
@ -246,7 +246,7 @@ namespace osu.Game.Beatmaps.Formats
} }
} }
private void handleEvents(string line) private void handleEvent(string line)
{ {
string[] split = line.Split(','); string[] split = line.Split(',');
@ -275,7 +275,9 @@ namespace osu.Game.Beatmaps.Formats
} }
} }
private void handleTimingPoints(string line) private void handleTimingPoint(string line)
{
try
{ {
string[] split = line.Split(','); string[] split = line.Split(',');
@ -360,8 +362,12 @@ namespace osu.Game.Beatmaps.Formats
}); });
} }
} }
catch (FormatException e)
{
}
}
private void handleHitObjects(string line) private void handleHitObject(string line)
{ {
// If the ruleset wasn't specified, assume the osu!standard ruleset. // If the ruleset wasn't specified, assume the osu!standard ruleset.
if (parser == null) if (parser == null)

View File

@ -0,0 +1,42 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
namespace osu.Game.Beatmaps.Legacy
{
[Flags]
public enum LegacyMods
{
None = 0,
NoFail = 1 << 0,
Easy = 1 << 1,
TouchDevice = 1 << 2,
Hidden = 1 << 3,
HardRock = 1 << 4,
SuddenDeath = 1 << 5,
DoubleTime = 1 << 6,
Relax = 1 << 7,
HalfTime = 1 << 8,
Nightcore = 1 << 9,
Flashlight = 1 << 10,
Autoplay = 1 << 11,
SpunOut = 1 << 12,
Autopilot = 1 << 13,
Perfect = 1 << 14,
Key4 = 1 << 15,
Key5 = 1 << 16,
Key6 = 1 << 17,
Key7 = 1 << 18,
Key8 = 1 << 19,
FadeIn = 1 << 20,
Random = 1 << 21,
Cinema = 1 << 22,
Target = 1 << 23,
Key9 = 1 << 24,
KeyCoop = 1 << 25,
Key1 = 1 << 26,
Key3 = 1 << 27,
Key2 = 1 << 28,
}
}

View File

@ -1,23 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Beatmaps
{
public enum RankStatus
{
Any = 7,
[Description("Ranked & Approved")]
RankedApproved = 0,
Approved = 1,
Loved = 8,
Favourites = 2,
[Description("Mod Requests")]
ModRequests = 3,
Pending = 4,
Graveyard = 5,
[Description("My Maps")]
MyMaps = 6,
}
}

View File

@ -84,6 +84,9 @@ namespace osu.Game.Configuration
Set(OsuSetting.Version, string.Empty); Set(OsuSetting.Version, string.Empty);
Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg);
Set(OsuSetting.ScreenshotCaptureMenuCursor, false);
Set(OsuSetting.SongSelectRightMouseScroll, false);
} }
public OsuConfigManager(Storage storage) : base(storage) public OsuConfigManager(Storage storage) : base(storage)
@ -128,6 +131,8 @@ namespace osu.Game.Configuration
ShowConvertedBeatmaps, ShowConvertedBeatmaps,
SpeedChangeVisualisation, SpeedChangeVisualisation,
Skin, Skin,
ScreenshotFormat ScreenshotFormat,
ScreenshotCaptureMenuCursor,
SongSelectRightMouseScroll
} }
} }

View File

@ -35,11 +35,13 @@ namespace osu.Game.Database
/// <summary> /// <summary>
/// Fired when a new <see cref="TModel"/> becomes available in the database. /// Fired when a new <see cref="TModel"/> becomes available in the database.
/// This is not guaranteed to run on the update thread.
/// </summary> /// </summary>
public event Action<TModel> ItemAdded; public event Action<TModel> ItemAdded;
/// <summary> /// <summary>
/// Fired when a <see cref="TModel"/> is removed from the database. /// Fired when a <see cref="TModel"/> is removed from the database.
/// This is not guaranteed to run on the update thread.
/// </summary> /// </summary>
public event Action<TModel> ItemRemoved; public event Action<TModel> ItemRemoved;

View File

@ -61,9 +61,9 @@ namespace osu.Game.Graphics.Containers
AddText(text.Substring(previousLinkEnd)); AddText(text.Substring(previousLinkEnd));
} }
public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null) public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action<SpriteText> creationParameters = null)
{ {
AddInternal(new DrawableLinkCompiler(AddText(text).ToList()) AddInternal(new DrawableLinkCompiler(AddText(text, creationParameters).ToList())
{ {
TooltipText = tooltipText ?? (url != text ? url : string.Empty), TooltipText = tooltipText ?? (url != text ? url : string.Empty),
Action = () => Action = () =>

View File

@ -0,0 +1,167 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using OpenTK.Graphics;
namespace osu.Game.Graphics.Containers
{
public class WaveContainer : VisibilityContainer
{
public const float APPEAR_DURATION = 800;
public const float DISAPPEAR_DURATION = 500;
private const Easing easing_show = Easing.OutSine;
private const Easing easing_hide = Easing.InSine;
private readonly Wave firstWave;
private readonly Wave secondWave;
private readonly Wave thirdWave;
private readonly Wave fourthWave;
private readonly Container<Wave> wavesContainer;
private readonly Container contentContainer;
protected override Container<Drawable> Content => contentContainer;
public Color4 FirstWaveColour
{
get => firstWave.Colour;
set => firstWave.Colour = value;
}
public Color4 SecondWaveColour
{
get => secondWave.Colour;
set => secondWave.Colour = value;
}
public Color4 ThirdWaveColour
{
get => thirdWave.Colour;
set => thirdWave.Colour = value;
}
public Color4 FourthWaveColour
{
get => fourthWave.Colour;
set => fourthWave.Colour = value;
}
public WaveContainer()
{
Masking = true;
AddInternal(wavesContainer = new Container<Wave>
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Masking = true,
Children = new[]
{
firstWave = new Wave
{
Rotation = 13,
FinalPosition = -930,
},
secondWave = new Wave
{
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
Rotation = -7,
FinalPosition = -560,
},
thirdWave = new Wave
{
Rotation = 4,
FinalPosition = -390,
},
fourthWave = new Wave
{
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
Rotation = -2,
FinalPosition = -220,
},
},
});
AddInternal(contentContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
});
}
protected override void PopIn()
{
foreach (var w in wavesContainer.Children)
w.State = Visibility.Visible;
this.FadeIn(100, Easing.OutQuint);
contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint);
this.FadeIn(100, Easing.OutQuint);
}
protected override void PopOut()
{
this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint);
contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, Easing.In);
foreach (var w in wavesContainer.Children)
w.State = Visibility.Hidden;
this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint);
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
// This is done as an optimization, such that invisible parts of the waves
// are masked away, and thus do not consume fill rate.
wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y));
}
private class Wave : VisibilityContainer
{
public float FinalPosition;
protected override bool StartHidden => true;
public Wave()
{
RelativeSizeAxes = Axes.X;
Width = 1.5f;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(50),
Radius = 20f,
};
Child = new Box { RelativeSizeAxes = Axes.Both };
}
protected override void Update()
{
base.Update();
// We can not use RelativeSizeAxes for Height, because the height
// of our parent diminishes as the content moves up.
Height = Parent.Parent.DrawSize.Y * 1.5f;
}
protected override void PopIn() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
protected override void PopOut() => this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide);
}
}
}

View File

@ -20,7 +20,7 @@ namespace osu.Game.Graphics.Cursor
/// <summary> /// <summary>
/// Whether any cursors can be displayed. /// Whether any cursors can be displayed.
/// </summary> /// </summary>
public bool CanShowCursor = true; internal bool CanShowCursor = true;
public CursorContainer Cursor { get; } public CursorContainer Cursor { get; }
public bool ProvidingUserCursor => true; public bool ProvidingUserCursor => true;

View File

@ -12,12 +12,16 @@ using osu.Framework.Input;
using osu.Game.Configuration; using osu.Game.Configuration;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
namespace osu.Game.Graphics.Cursor namespace osu.Game.Graphics.Cursor
{ {
public class MenuCursor : CursorContainer public class MenuCursor : CursorContainer
{ {
private readonly IBindable<bool> screenshotCursorVisibility = new Bindable<bool>(true);
public override bool IsPresent => screenshotCursorVisibility.Value && base.IsPresent;
protected override Drawable CreateCursor() => new Cursor(); protected override Drawable CreateCursor() => new Cursor();
private Bindable<bool> cursorRotate; private Bindable<bool> cursorRotate;
@ -25,6 +29,15 @@ namespace osu.Game.Graphics.Cursor
private bool startRotation; private bool startRotation;
[BackgroundDependencyLoader(true)]
private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager)
{
cursorRotate = config.GetBindable<bool>(OsuSetting.CursorRotation);
if (screenshotManager != null)
screenshotCursorVisibility.BindTo(screenshotManager.CursorVisibility);
}
protected override bool OnMouseMove(InputState state) protected override bool OnMouseMove(InputState state)
{ {
if (cursorRotate && dragging) if (cursorRotate && dragging)
@ -104,12 +117,6 @@ namespace osu.Game.Graphics.Cursor
ActiveCursor.ScaleTo(0.6f, 250, Easing.In); ActiveCursor.ScaleTo(0.6f, 250, Easing.In);
} }
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
cursorRotate = config.GetBindable<bool>(OsuSetting.CursorRotation);
}
public class Cursor : Container public class Cursor : Container
{ {
private Container cursorContainer; private Container cursorContainer;

View File

@ -4,6 +4,8 @@
using System; using System;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
@ -12,6 +14,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Threading;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -21,7 +24,17 @@ namespace osu.Game.Graphics
{ {
public class ScreenshotManager : Container, IKeyBindingHandler<GlobalAction>, IHandleGlobalInput public class ScreenshotManager : Container, IKeyBindingHandler<GlobalAction>, IHandleGlobalInput
{ {
private readonly BindableBool cursorVisibility = new BindableBool(true);
/// <summary>
/// Changed when screenshots are being or have finished being taken, to control whether cursors should be visible.
/// If cursors should not be visible, cursors have 3 frames to hide themselves.
/// </summary>
public IBindable<bool> CursorVisibility => cursorVisibility;
private Bindable<ScreenshotFormat> screenshotFormat; private Bindable<ScreenshotFormat> screenshotFormat;
private Bindable<bool> captureMenuCursor;
private GameHost host; private GameHost host;
private Storage storage; private Storage storage;
private NotificationOverlay notificationOverlay; private NotificationOverlay notificationOverlay;
@ -36,6 +49,7 @@ namespace osu.Game.Graphics
this.notificationOverlay = notificationOverlay; this.notificationOverlay = notificationOverlay;
screenshotFormat = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat); screenshotFormat = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat);
captureMenuCursor = config.GetBindable<bool>(OsuSetting.ScreenshotCaptureMenuCursor);
shutter = audio.Sample.Get("UI/shutter"); shutter = audio.Sample.Get("UI/shutter");
} }
@ -55,10 +69,31 @@ namespace osu.Game.Graphics
public bool OnReleased(GlobalAction action) => false; public bool OnReleased(GlobalAction action) => false;
public async void TakeScreenshotAsync() private volatile int screenShotTasks;
public async Task TakeScreenshotAsync() => await Task.Run(async () =>
{ {
Interlocked.Increment(ref screenShotTasks);
if (!captureMenuCursor.Value)
{
cursorVisibility.Value = false;
// We need to wait for at most 3 draw nodes to be drawn, following which we can be assured at least one DrawNode has been generated/drawn with the set value
const int frames_to_wait = 3;
int framesWaited = 0;
ScheduledDelegate waitDelegate = host.DrawThread.Scheduler.AddDelayed(() => framesWaited++, 0, true);
while (framesWaited < frames_to_wait)
Thread.Sleep(10);
waitDelegate.Cancel();
}
using (var bitmap = await host.TakeScreenshotAsync()) using (var bitmap = await host.TakeScreenshotAsync())
{ {
Interlocked.Decrement(ref screenShotTasks);
var fileName = getFileName(); var fileName = getFileName();
if (fileName == null) return; if (fileName == null) return;
@ -86,6 +121,14 @@ namespace osu.Game.Graphics
} }
}); });
} }
});
protected override void Update()
{
base.Update();
if (cursorVisibility == false && Interlocked.CompareExchange(ref screenShotTasks, 0, 0) == 0)
cursorVisibility.Value = true;
} }
private string getFileName() private string getFileName()

View File

@ -10,7 +10,7 @@ namespace osu.Game.Graphics.UserInterface
{ {
public BackButton() public BackButton()
{ {
Text = @"Back"; Text = @"back";
Icon = FontAwesome.fa_osu_left_o; Icon = FontAwesome.fa_osu_left_o;
Anchor = Anchor.BottomLeft; Anchor = Anchor.BottomLeft;
Origin = Anchor.BottomLeft; Origin = Anchor.BottomLeft;

View File

@ -17,6 +17,8 @@ namespace osu.Game.Graphics.UserInterface
protected override TabItem<T> CreateTabItem(T value) => new BreadcrumbTabItem(value); protected override TabItem<T> CreateTabItem(T value) => new BreadcrumbTabItem(value);
protected override float StripWidth() => base.StripWidth() - (padding + 8);
public BreadcrumbControl() public BreadcrumbControl()
{ {
Height = 26; Height = 26;

View File

@ -14,22 +14,36 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
{ {
public class OsuTabControl<T> : TabControl<T> public class OsuTabControl<T> : TabControl<T>
{ {
private readonly Box strip;
protected override Dropdown<T> CreateDropdown() => new OsuTabDropdown(); protected override Dropdown<T> CreateDropdown() => new OsuTabDropdown();
protected override TabItem<T> CreateTabItem(T value) => new OsuTabItem(value); protected override TabItem<T> CreateTabItem(T value) => new OsuTabItem(value);
protected virtual float StripWidth() => TabContainer.Children.Sum(c => c.IsPresent ? c.DrawWidth + TabContainer.Spacing.X : 0) - TabContainer.Spacing.X;
protected virtual float StripHeight() => 1;
private static bool isEnumType => typeof(T).IsEnum; private static bool isEnumType => typeof(T).IsEnum;
public OsuTabControl() public OsuTabControl()
{ {
TabContainer.Spacing = new Vector2(10f, 0f); TabContainer.Spacing = new Vector2(10f, 0f);
Add(strip = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Height = StripHeight(),
Colour = Color4.White.Opacity(0),
});
if (isEnumType) if (isEnumType)
foreach (var val in (T[])Enum.GetValues(typeof(T))) foreach (var val in (T[])Enum.GetValues(typeof(T)))
AddItem(val); AddItem(val);
@ -57,6 +71,12 @@ namespace osu.Game.Graphics.UserInterface
} }
} }
public Color4 StripColour
{
get => strip.Colour;
set => strip.Colour = value;
}
protected override TabFillFlowContainer CreateTabFlow() => new OsuTabFillFlowContainer protected override TabFillFlowContainer CreateTabFlow() => new OsuTabFillFlowContainer
{ {
Direction = FillDirection.Full, Direction = FillDirection.Full,
@ -65,6 +85,15 @@ namespace osu.Game.Graphics.UserInterface
Masking = true Masking = true
}; };
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
// dont bother calculating if the strip is invisible
if (strip.Colour.MaxAlpha > 0)
strip.Width = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000), strip.Width, StripWidth(), 0, 500, Easing.OutQuint);
}
public class OsuTabItem : TabItem<T>, IHasAccentColour public class OsuTabItem : TabItem<T>, IHasAccentColour
{ {
protected readonly SpriteText Text; protected readonly SpriteText Text;

View File

@ -45,7 +45,7 @@ namespace osu.Game.Input.Bindings
}; };
protected override IEnumerable<Drawable> KeyBindingInputQueue => protected override IEnumerable<Drawable> KeyBindingInputQueue =>
handler == null ? base.KeyBindingInputQueue : new[] { handler }.Concat(base.KeyBindingInputQueue); handler == null ? base.KeyBindingInputQueue : base.KeyBindingInputQueue.Prepend(handler);
} }
public enum GlobalAction public enum GlobalAction

View File

@ -13,9 +13,9 @@ namespace osu.Game.Input.Handlers
public abstract class ReplayInputHandler : InputHandler public abstract class ReplayInputHandler : InputHandler
{ {
/// <summary> /// <summary>
/// A function provided to convert replay coordinates from gamefield to screen space. /// A function that converts coordinates from gamefield to screen space.
/// </summary> /// </summary>
public Func<Vector2, Vector2> ToScreenSpace { protected get; set; } public Func<Vector2, Vector2> GamefieldToScreenSpace { protected get; set; }
/// <summary> /// <summary>
/// Update the current frame based on an incoming time value. /// Update the current frame based on an incoming time value.

View File

@ -37,13 +37,7 @@ namespace osu.Game.Online.API
public Bindable<User> LocalUser { get; } = new Bindable<User>(createGuestUser()); public Bindable<User> LocalUser { get; } = new Bindable<User>(createGuestUser());
public string Token protected bool HasLogin => authentication.Token.Value != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password);
{
get { return authentication.Token?.ToString(); }
set { authentication.Token = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); }
}
protected bool HasLogin => Token != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password);
private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource();
@ -57,11 +51,15 @@ namespace osu.Game.Online.API
log = Logger.GetLogger(LoggingTarget.Network); log = Logger.GetLogger(LoggingTarget.Network);
ProvidedUsername = config.Get<string>(OsuSetting.Username); ProvidedUsername = config.Get<string>(OsuSetting.Username);
Token = config.Get<string>(OsuSetting.Token);
authentication.TokenString = config.Get<string>(OsuSetting.Token);
authentication.Token.ValueChanged += onTokenChanged;
Task.Factory.StartNew(run, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); Task.Factory.StartNew(run, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
} }
private void onTokenChanged(OAuthToken token) => config.Set(OsuSetting.Token, config.Get<bool>(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty);
private readonly List<IOnlineComponent> components = new List<IOnlineComponent>(); private readonly List<IOnlineComponent> components = new List<IOnlineComponent>();
internal new void Schedule(Action action) => base.Schedule(action); internal new void Schedule(Action action) => base.Schedule(action);
@ -306,9 +304,6 @@ namespace osu.Game.Online.API
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);
config.Set(OsuSetting.Token, config.Get<bool>(OsuSetting.SavePassword) ? Token : string.Empty);
config.Save();
flushQueue(); flushQueue();
cancellationToken.Cancel(); cancellationToken.Cancel();
} }

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Diagnostics; using System.Diagnostics;
using osu.Framework.Configuration;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
namespace osu.Game.Online.API namespace osu.Game.Online.API
@ -12,7 +13,13 @@ namespace osu.Game.Online.API
private readonly string clientSecret; private readonly string clientSecret;
private readonly string endpoint; private readonly string endpoint;
public OAuthToken Token; public readonly Bindable<OAuthToken> Token = new Bindable<OAuthToken>();
public string TokenString
{
get => Token.Value?.ToString();
set => Token.Value = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value);
}
internal OAuth(string clientId, string clientSecret, string endpoint) internal OAuth(string clientId, string clientSecret, string endpoint)
{ {
@ -47,7 +54,7 @@ namespace osu.Game.Online.API
return false; return false;
} }
Token = req.ResponseObject; Token.Value = req.ResponseObject;
return true; return true;
} }
} }
@ -66,14 +73,14 @@ namespace osu.Game.Online.API
{ {
req.Perform(); req.Perform();
Token = req.ResponseObject; Token.Value = req.ResponseObject;
return true; return true;
} }
} }
catch catch
{ {
//todo: potentially only kill the refresh token on certain exception types. //todo: potentially only kill the refresh token on certain exception types.
Token = null; Token.Value = null;
return false; return false;
} }
} }
@ -95,15 +102,15 @@ namespace osu.Game.Online.API
if (accessTokenValid) return true; if (accessTokenValid) return true;
// if not, let's try using our refresh token to request a new access token. // if not, let's try using our refresh token to request a new access token.
if (!string.IsNullOrEmpty(Token?.RefreshToken)) if (!string.IsNullOrEmpty(Token.Value?.RefreshToken))
// ReSharper disable once PossibleNullReferenceException // ReSharper disable once PossibleNullReferenceException
AuthenticateWithRefresh(Token.RefreshToken); AuthenticateWithRefresh(Token.Value.RefreshToken);
return accessTokenValid; return accessTokenValid;
} }
} }
private bool accessTokenValid => Token?.IsValid ?? false; private bool accessTokenValid => Token.Value?.IsValid ?? false;
internal bool HasValidAccessToken => RequestAccessToken() != null; internal bool HasValidAccessToken => RequestAccessToken() != null;
@ -111,12 +118,12 @@ namespace osu.Game.Online.API
{ {
if (!ensureAccessToken()) return null; if (!ensureAccessToken()) return null;
return Token.AccessToken; return Token.Value.AccessToken;
} }
internal void Clear() internal void Clear()
{ {
Token = null; Token.Value = null;
} }
private class AccessTokenRequestRefresh : AccessTokenRequest private class AccessTokenRequestRefresh : AccessTokenRequest

View File

@ -30,6 +30,9 @@ namespace osu.Game.Online.API.Requests
[JsonProperty(@"video")] [JsonProperty(@"video")]
private bool hasVideo { get; set; } private bool hasVideo { get; set; }
[JsonProperty(@"status")]
private BeatmapSetOnlineStatus status { get; set; }
[JsonProperty(@"submitted_date")] [JsonProperty(@"submitted_date")]
private DateTimeOffset submitted { get; set; } private DateTimeOffset submitted { get; set; }
@ -60,6 +63,7 @@ namespace osu.Game.Online.API.Requests
PlayCount = playCount, PlayCount = playCount,
FavouriteCount = favouriteCount, FavouriteCount = favouriteCount,
BPM = bpm, BPM = bpm,
Status = status,
HasVideo = hasVideo, HasVideo = hasVideo,
Submitted = submitted, Submitted = submitted,
Ranked = ranked, Ranked = ranked,

View File

@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps; using System.ComponentModel;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Direct; using osu.Game.Overlays.Direct;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -13,21 +13,36 @@ namespace osu.Game.Online.API.Requests
{ {
private readonly string query; private readonly string query;
private readonly RulesetInfo ruleset; private readonly RulesetInfo ruleset;
private readonly RankStatus rankStatus; private readonly BeatmapSearchCategory searchCategory;
private readonly DirectSortCriteria sortCriteria; private readonly DirectSortCriteria sortCriteria;
private readonly SortDirection direction; private readonly SortDirection direction;
private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc"; private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc";
public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, RankStatus rankStatus = RankStatus.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending) public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory searchCategory = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending)
{ {
this.query = System.Uri.EscapeDataString(query); this.query = System.Uri.EscapeDataString(query);
this.ruleset = ruleset; this.ruleset = ruleset;
this.rankStatus = rankStatus; this.searchCategory = searchCategory;
this.sortCriteria = sortCriteria; this.sortCriteria = sortCriteria;
this.direction = direction; this.direction = direction;
} }
// ReSharper disable once ImpureMethodCallOnReadonlyValueField // ReSharper disable once ImpureMethodCallOnReadonlyValueField
protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)rankStatus}&sort={sortCriteria.ToString().ToLower()}_{directionString}"; protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)searchCategory}&sort={sortCriteria.ToString().ToLower()}_{directionString}";
}
public enum BeatmapSearchCategory
{
Any = 7,
[Description("Ranked & Approved")]
RankedApproved = 0,
Approved = 1,
Loved = 8,
Favourites = 2,
Qualified = 3,
Pending = 4,
Graveyard = 5,
[Description("My Maps")]
MyMaps = 6,
} }
} }

View File

@ -36,6 +36,10 @@ using osu.Game.Overlays.Volume;
namespace osu.Game namespace osu.Game
{ {
/// <summary>
/// The full osu! experience. Builds on top of <see cref="OsuGameBase"/> to add menus and binding logic
/// for initial components that are generally retrieved via DI.
/// </summary>
public class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction> public class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>
{ {
public Toolbar Toolbar; public Toolbar Toolbar;
@ -56,6 +60,8 @@ namespace osu.Game
private BeatmapSetOverlay beatmapSetOverlay; private BeatmapSetOverlay beatmapSetOverlay;
private ScreenshotManager screenshotManager;
public virtual Storage GetStorageForStableInstall() => null; public virtual Storage GetStorageForStableInstall() => null;
private Intro intro private Intro intro
@ -152,7 +158,7 @@ namespace osu.Game
/// Show a beatmap set as an overlay. /// Show a beatmap set as an overlay.
/// </summary> /// </summary>
/// <param name="setId">The set to display.</param> /// <param name="setId">The set to display.</param>
public void ShowBeatmapSet(int setId) => beatmapSetOverlay.ShowBeatmapSet(setId); public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId);
/// <summary> /// <summary>
/// Show a user's profile as an overlay. /// Show a user's profile as an overlay.
@ -190,12 +196,16 @@ namespace osu.Game
} }
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap);
Beatmap.Value.Mods.Value = s.Mods;
menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay))); menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay)));
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
// this needs to be cached before base.LoadComplete as it is used by CursorOverrideContainer.
dependencies.Cache(screenshotManager = new ScreenshotManager());
base.LoadComplete(); base.LoadComplete();
// The next time this is updated is in UpdateAfterChildren, which occurs too late and results // The next time this is updated is in UpdateAfterChildren, which occurs too late and results
@ -239,7 +249,8 @@ namespace osu.Game
loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add); loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add);
loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add); loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
loadComponentSingleFile(new ScreenshotManager(), Add);
loadComponentSingleFile(screenshotManager, Add);
//overlay elements //overlay elements
loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
@ -435,7 +446,7 @@ namespace osu.Game
sensitivity.Value = 1; sensitivity.Value = 1;
sensitivity.Disabled = true; sensitivity.Disabled = true;
frameworkConfig.Set(FrameworkSetting.ActiveInputHandlers, string.Empty); frameworkConfig.Set(FrameworkSetting.IgnoredInputHandlers, string.Empty);
frameworkConfig.GetBindable<ConfineMouseMode>(FrameworkSetting.ConfineMouseMode).SetDefault(); frameworkConfig.GetBindable<ConfineMouseMode>(FrameworkSetting.ConfineMouseMode).SetDefault();
return true; return true;
case GlobalAction.ToggleToolbar: case GlobalAction.ToggleToolbar:

View File

@ -34,6 +34,11 @@ using osu.Game.Skinning;
namespace osu.Game namespace osu.Game
{ {
/// <summary>
/// The most basic <see cref="Game"/> that can be used to host osu! components and systems.
/// Unlike <see cref="OsuGame"/>, this class will not load any kind of UI, allowing it to be used
/// for provide dependencies to test cases without interfering with them.
/// </summary>
public class OsuGameBase : Framework.Game, ICanAcceptFiles public class OsuGameBase : Framework.Game, ICanAcceptFiles
{ {
protected OsuConfigManager LocalConfig; protected OsuConfigManager LocalConfig;

View File

@ -23,9 +23,8 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly ClickableArea clickableArea; private readonly ClickableArea clickableArea;
private readonly FillFlowContainer fields; private readonly FillFlowContainer fields;
private UserProfileOverlay profile;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -34,28 +33,36 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
var i = BeatmapSet.OnlineInfo; updateDisplay();
}
}
avatar.User = BeatmapSet.Metadata.Author; private void updateDisplay()
clickableArea.Action = () => profile?.ShowUser(avatar.User); {
avatar.User = BeatmapSet?.Metadata.Author;
fields.Clear();
if (BeatmapSet == null)
return;
var online = BeatmapSet.OnlineInfo;
fields.Children = new Drawable[] fields.Children = new Drawable[]
{ {
new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"), new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"),
new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold") new Field("submitted on", online.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")
{ {
Margin = new MarginPadding { Top = 5 }, Margin = new MarginPadding { Top = 5 },
}, },
}; };
if (i.Ranked.HasValue) if (online.Ranked.HasValue)
{ {
fields.Add(new Field("ranked on ", i.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); fields.Add(new Field("ranked on ", online.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
} }
else if (i.LastUpdated.HasValue) else if (online.LastUpdated.HasValue)
{ {
fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); fields.Add(new Field("last updated on ", online.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
}
} }
} }
@ -73,6 +80,7 @@ namespace osu.Game.Overlays.BeatmapSet
Masking = true, Masking = true,
Child = avatar = new UpdateableAvatar Child = avatar = new UpdateableAvatar
{ {
ShowGuestOnNull = false,
Size = new Vector2(height), Size = new Vector2(height),
}, },
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
@ -95,8 +103,12 @@ namespace osu.Game.Overlays.BeatmapSet
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profile) private void load(UserProfileOverlay profile)
{ {
this.profile = profile; clickableArea.Action = () =>
clickableArea.Action = () => profile?.ShowUser(avatar.User); {
if (avatar.User != null) profile?.ShowUser(avatar.User);
};
updateDisplay();
} }
private class Field : FillFlowContainer private class Field : FillFlowContainer

View File

@ -18,6 +18,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Statistic length, bpm, circleCount, sliderCount; private readonly Statistic length, bpm, circleCount, sliderCount;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -26,11 +27,12 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##"); updateDisplay();
} }
} }
private BeatmapInfo beatmap; private BeatmapInfo beatmap;
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
{ {
get { return beatmap; } get { return beatmap; }
@ -39,6 +41,22 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmap) return; if (value == beatmap) return;
beatmap = value; beatmap = value;
updateDisplay();
}
}
private void updateDisplay()
{
bpm.Value = BeatmapSet?.OnlineInfo.BPM.ToString(@"0.##") ?? "-";
if (beatmap == null)
{
length.Value = string.Empty;
circleCount.Value = string.Empty;
sliderCount.Value = string.Empty;
}
else
{
length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss"); length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss");
circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString();
sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString(); sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString();
@ -62,12 +80,19 @@ namespace osu.Game.Overlays.BeatmapSet
}; };
} }
[BackgroundDependencyLoader]
private void load()
{
updateDisplay();
}
private class Statistic : Container, IHasTooltip private class Statistic : Container, IHasTooltip
{ {
private readonly string name; private readonly string name;
private readonly OsuSpriteText value; private readonly OsuSpriteText value;
public string TooltipText => name; public string TooltipText => name;
public string Value public string Value
{ {
get { return value.Text; } get { return value.Text; }

View File

@ -41,10 +41,17 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
Beatmap.Value = BeatmapSet.Beatmaps.First(); updateDisplay();
plays.Value = BeatmapSet.OnlineInfo.PlayCount; }
favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount; }
difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.Select(b => new DifficultySelectorButton(b)
private void updateDisplay()
{
difficulties.Clear();
if (BeatmapSet != null)
{
difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b)
{ {
State = DifficultySelectorState.NotSelected, State = DifficultySelectorState.NotSelected,
OnHovered = beatmap => OnHovered = beatmap =>
@ -53,15 +60,17 @@ namespace osu.Game.Overlays.BeatmapSet
starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##"); starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##");
starRating.FadeIn(100); starRating.FadeIn(100);
}, },
OnClicked = beatmap => OnClicked = beatmap => { Beatmap.Value = beatmap; },
{
Beatmap.Value = beatmap;
},
}); });
}
starRating.FadeOut(100);
Beatmap.Value = BeatmapSet?.Beatmaps.FirstOrDefault();
plays.Value = BeatmapSet?.OnlineInfo.PlayCount ?? 0;
favourites.Value = BeatmapSet?.OnlineInfo.FavouriteCount ?? 0;
updateDifficultyButtons(); updateDifficultyButtons();
} }
}
public BeatmapPicker() public BeatmapPicker()
{ {
@ -140,6 +149,7 @@ namespace osu.Game.Overlays.BeatmapSet
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
starRating.Colour = colours.Yellow; starRating.Colour = colours.Yellow;
updateDisplay();
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -150,7 +160,10 @@ namespace osu.Game.Overlays.BeatmapSet
Beatmap.TriggerChange(); Beatmap.TriggerChange();
} }
private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version; private void showBeatmap(BeatmapInfo beatmap)
{
version.Text = beatmap?.Version;
}
private void updateDifficultyButtons() private void updateDifficultyButtons()
{ {

View File

@ -7,7 +7,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using OpenTK; using OpenTK;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class DownloadButton : HeaderButton public class DownloadButton : HeaderButton
{ {

View File

@ -10,7 +10,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using OpenTK; using OpenTK;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class FavouriteButton : HeaderButton public class FavouriteButton : HeaderButton
{ {

View File

@ -1,12 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class HeaderButton : TriangleButton public class HeaderButton : TriangleButton
{ {

View File

@ -3,6 +3,7 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -11,12 +12,11 @@ using osu.Framework.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Direct;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Overlays.Direct;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class PreviewButton : OsuClickableContainer public class PreviewButton : OsuClickableContainer
{ {
@ -85,6 +85,8 @@ namespace osu.Game.Overlays.BeatmapSet
// prevent negative (potential infinite) width if a track without length was loaded // prevent negative (potential infinite) width if a track without length was loaded
progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f; progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f;
} }
else
progress.Width = 0;
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)

View File

@ -1,11 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
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.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Overlays.BeatmapSet.Buttons;
using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Details;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -20,6 +22,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly UserRatings ratings; private readonly UserRatings ratings;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -33,19 +36,24 @@ namespace osu.Game.Overlays.BeatmapSet
} }
private BeatmapInfo beatmap; private BeatmapInfo beatmap;
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
{ {
get { return beatmap; } get { return beatmap; }
set set
{ {
if (value == beatmap) return; if (value == beatmap) return;
beatmap = value;
basic.Beatmap = advanced.Beatmap = Beatmap; basic.Beatmap = advanced.Beatmap = beatmap = value;
ratings.Metrics = Beatmap.Metrics; updateDisplay();
} }
} }
private void updateDisplay()
{
ratings.Metrics = Beatmap?.Metrics;
}
public Details() public Details()
{ {
Width = BeatmapSetOverlay.RIGHT_WIDTH; Width = BeatmapSetOverlay.RIGHT_WIDTH;
@ -88,6 +96,12 @@ namespace osu.Game.Overlays.BeatmapSet
}; };
} }
[BackgroundDependencyLoader]
private void load()
{
updateDisplay();
}
public void StopPreview() => preview.Playing.Value = false; public void StopPreview() => preview.Playing.Value = false;
private class DetailBox : Container private class DetailBox : Container

View File

@ -11,6 +11,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.BeatmapSet.Buttons;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -18,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
public class Header : Container public class Header : Container
{ {
private const float transition_duration = 250; private const float transition_duration = 200;
private const float tabs_height = 50; private const float tabs_height = 50;
private const float buttons_height = 45; private const float buttons_height = 45;
private const float buttons_spacing = 5; private const float buttons_spacing = 5;
@ -30,15 +31,17 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly FillFlowContainer videoButtons; private readonly FillFlowContainer videoButtons;
private readonly AuthorInfo author; private readonly AuthorInfo author;
private readonly Container downloadButtonsContainer; private readonly Container downloadButtonsContainer;
private readonly BeatmapSetOnlineStatusPill onlineStatusPill;
public Details Details; public Details Details;
private BeatmapManager beatmaps; private BeatmapManager beatmaps;
private DelayedLoadWrapper cover; private DelayedLoadWrapper cover;
public readonly BeatmapPicker Picker; public readonly BeatmapPicker Picker;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
private readonly FavouriteButton favouriteButton;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -48,14 +51,26 @@ namespace osu.Game.Overlays.BeatmapSet
beatmapSet = value; beatmapSet = value;
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet; Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet;
title.Text = BeatmapSet.Metadata.Title;
artist.Text = BeatmapSet.Metadata.Artist;
downloadButtonsContainer.FadeIn(); updateDisplay();
}
}
private void updateDisplay()
{
title.Text = BeatmapSet?.Metadata.Title ?? string.Empty;
artist.Text = BeatmapSet?.Metadata.Artist ?? string.Empty;
onlineStatusPill.Status = BeatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
cover?.FadeOut(400, Easing.Out);
if (BeatmapSet != null)
{
downloadButtonsContainer.FadeIn(transition_duration);
favouriteButton.FadeIn(transition_duration);
noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration); noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration);
videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration); videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration);
cover?.FadeOut(400, Easing.Out);
coverContainer.Add(cover = new DelayedLoadWrapper( coverContainer.Add(cover = new DelayedLoadWrapper(
new BeatmapSetCover(BeatmapSet) new BeatmapSetCover(BeatmapSet)
{ {
@ -69,6 +84,11 @@ namespace osu.Game.Overlays.BeatmapSet
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}); });
} }
else
{
downloadButtonsContainer.FadeOut(transition_duration);
favouriteButton.FadeOut(transition_duration);
}
} }
public Header() public Header()
@ -164,7 +184,7 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Top = 10 }, Margin = new MarginPadding { Top = 10 },
Children = new Drawable[] Children = new Drawable[]
{ {
new FavouriteButton(), favouriteButton = new FavouriteButton(),
downloadButtonsContainer = new Container downloadButtonsContainer = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -204,11 +224,23 @@ namespace osu.Game.Overlays.BeatmapSet
}, },
}, },
}, },
Details = new Details new FillFlowContainer
{ {
Anchor = Anchor.BottomRight, Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight, Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Right = BeatmapSetOverlay.X_PADDING }, Margin = new MarginPadding { Right = BeatmapSetOverlay.X_PADDING },
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
Children = new Drawable[]
{
onlineStatusPill = new BeatmapSetOnlineStatusPill(14, new MarginPadding { Horizontal = 25, Vertical = 8 })
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
Details = new Details(),
},
}, },
}, },
}, },
@ -224,6 +256,8 @@ namespace osu.Game.Overlays.BeatmapSet
this.beatmaps = beatmaps; this.beatmaps = beatmaps;
beatmaps.ItemAdded += handleBeatmapAdd; beatmaps.ItemAdded += handleBeatmapAdd;
updateDisplay();
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
@ -232,11 +266,11 @@ namespace osu.Game.Overlays.BeatmapSet
if (beatmaps != null) beatmaps.ItemAdded -= handleBeatmapAdd; if (beatmaps != null) beatmaps.ItemAdded -= handleBeatmapAdd;
} }
private void handleBeatmapAdd(BeatmapSetInfo beatmap) private void handleBeatmapAdd(BeatmapSetInfo beatmap) => Schedule(() =>
{ {
if (beatmap.OnlineBeatmapSetID == BeatmapSet?.OnlineBeatmapSetID) if (beatmap.OnlineBeatmapSetID == BeatmapSet?.OnlineBeatmapSetID)
downloadButtonsContainer.FadeOut(transition_duration); downloadButtonsContainer.FadeOut(transition_duration);
} });
private void download(bool noVideo) private void download(bool noVideo)
{ {

View File

@ -34,11 +34,16 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
source.Text = BeatmapSet.Metadata.Source; updateDisplay();
tags.Text = BeatmapSet.Metadata.Tags;
} }
} }
private void updateDisplay()
{
source.Text = BeatmapSet?.Metadata.Source ?? string.Empty;
tags.Text = BeatmapSet?.Metadata.Tags ?? string.Empty;
}
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
{ {
get { return successRate.Beatmap; } get { return successRate.Beatmap; }
@ -132,6 +137,8 @@ namespace osu.Game.Overlays.BeatmapSet
successRateBackground.Colour = colours.GrayE; successRateBackground.Colour = colours.GrayE;
source.TextColour = description.TextColour = colours.Gray5; source.TextColour = description.TextColour = colours.Gray5;
tags.TextColour = colours.BlueDark; tags.TextColour = colours.BlueDark;
updateDisplay();
} }
private class MetadataSection : FillFlowContainer private class MetadataSection : FillFlowContainer

View File

@ -2,15 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK; using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
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.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
namespace osu.Game.Overlays.BeatmapSet.Scores namespace osu.Game.Overlays.BeatmapSet.Scores
{ {
@ -22,33 +22,60 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private readonly FillFlowContainer flow; private readonly FillFlowContainer flow;
private readonly DrawableTopScore topScore; private readonly DrawableTopScore topScore;
private readonly LoadingAnimation loadingAnimation; private readonly LoadingAnimation loadingAnimation;
private readonly Box foreground;
private bool isLoading; private bool loading
public bool IsLoading
{ {
get { return isLoading; } set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration);
set
{
if (isLoading == value) return;
isLoading = value;
foreground.FadeTo(isLoading ? 1 : 0, fade_duration);
loadingAnimation.FadeTo(isLoading ? 1 : 0, fade_duration);
}
} }
private IEnumerable<OnlineScore> scores; private IEnumerable<OnlineScore> scores;
private BeatmapInfo beatmap;
public IEnumerable<OnlineScore> Scores public IEnumerable<OnlineScore> Scores
{ {
get { return scores; } get { return scores; }
set set
{ {
getScoresRequest?.Cancel();
scores = value; scores = value;
var scoresAmount = scores.Count();
if (scoresAmount == 0) updateDisplay();
}
}
private GetScoresRequest getScoresRequest;
private APIAccess api;
public BeatmapInfo Beatmap
{ {
CleanAllScores(); get => beatmap;
set
{
beatmap = value;
Scores = null;
if (beatmap?.OnlineBeatmapID.HasValue != true)
return;
loading = true;
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
getScoresRequest.Success += r => Scores = r.Scores;
api.Queue(getScoresRequest);
}
}
private void updateDisplay()
{
loading = false;
var scoreCount = scores?.Count() ?? 0;
if (scoreCount == 0)
{
topScore.Hide();
flow.Clear();
return; return;
} }
@ -57,13 +84,12 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
flow.Clear(); flow.Clear();
if (scoresAmount < 2) if (scoreCount < 2)
return; return;
for (int i = 1; i < scoresAmount; i++) for (int i = 1; i < scoreCount; i++)
flow.Add(new DrawableScore(i, scores.ElementAt(i))); flow.Add(new DrawableScore(i, scores.ElementAt(i)));
} }
}
public ScoresContainer() public ScoresContainer()
{ {
@ -93,23 +119,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
}, },
} }
}, },
foreground = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.7f),
Alpha = 0,
},
loadingAnimation = new LoadingAnimation loadingAnimation = new LoadingAnimation
{ {
Alpha = 0, Alpha = 0,
Margin = new MarginPadding(20)
}, },
}; };
} }
public void CleanAllScores() [BackgroundDependencyLoader]
private void load(APIAccess api)
{ {
topScore.Hide(); this.api = api;
flow.Clear(); updateDisplay();
} }
} }
} }

View File

@ -29,16 +29,21 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmap) return; if (value == beatmap) return;
beatmap = value; beatmap = value;
int passCount = beatmap.OnlineInfo.PassCount; updateDisplay();
int playCount = beatmap.OnlineInfo.PlayCount; }
}
private void updateDisplay()
{
int passCount = beatmap?.OnlineInfo.PassCount ?? 0;
int playCount = beatmap?.OnlineInfo.PlayCount ?? 0;
var rate = playCount != 0 ? (float)passCount / playCount : 0; var rate = playCount != 0 ? (float)passCount / playCount : 0;
successPercent.Text = rate.ToString("P0"); successPercent.Text = rate.ToString("P0");
successRate.Length = rate; successRate.Length = rate;
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
graph.Metrics = Beatmap.Metrics; graph.Metrics = beatmap?.Metrics;
}
} }
public SuccessRate() public SuccessRate()
@ -74,7 +79,6 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Text = @"0%",
TextSize = 13, TextSize = 13,
}, },
}, },
@ -103,6 +107,8 @@ namespace osu.Game.Overlays.BeatmapSet
successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5; successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5;
successRate.AccentColour = colours.Green; successRate.AccentColour = colours.Green;
successRate.BackgroundColour = colours.GrayD; successRate.BackgroundColour = colours.GrayD;
updateDisplay();
} }
protected override void UpdateAfterChildren() protected override void UpdateAfterChildren()

View File

@ -27,11 +27,8 @@ namespace osu.Game.Overlays
private readonly Header header; private readonly Header header;
private readonly Info info; private readonly Info info;
private readonly ScoresContainer scores;
private APIAccess api; private APIAccess api;
private RulesetStore rulesets; private RulesetStore rulesets;
private GetScoresRequest getScoresRequest;
private readonly ScrollContainer scroll; private readonly ScrollContainer scroll;
@ -40,10 +37,11 @@ namespace osu.Game.Overlays
public BeatmapSetOverlay() public BeatmapSetOverlay()
{ {
FirstWaveColour = OsuColour.Gray(0.4f); ScoresContainer scores;
SecondWaveColour = OsuColour.Gray(0.3f); Waves.FirstWaveColour = OsuColour.Gray(0.4f);
ThirdWaveColour = OsuColour.Gray(0.2f); Waves.SecondWaveColour = OsuColour.Gray(0.3f);
FourthWaveColour = OsuColour.Gray(0.1f); Waves.ThirdWaveColour = OsuColour.Gray(0.2f);
Waves.FourthWaveColour = OsuColour.Gray(0.1f);
Anchor = Anchor.TopCentre; Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;
@ -88,31 +86,10 @@ namespace osu.Game.Overlays
header.Picker.Beatmap.ValueChanged += b => header.Picker.Beatmap.ValueChanged += b =>
{ {
info.Beatmap = b; info.Beatmap = b;
updateScores(b); scores.Beatmap = b;
}; };
} }
private void updateScores(BeatmapInfo beatmap)
{
getScoresRequest?.Cancel();
if (!beatmap.OnlineBeatmapID.HasValue)
{
scores.CleanAllScores();
return;
}
scores.IsLoading = true;
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
getScoresRequest.Success += r =>
{
scores.Scores = r.Scores;
scores.IsLoading = false;
};
api.Queue(getScoresRequest);
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets) private void load(APIAccess api, RulesetStore rulesets)
{ {
@ -123,14 +100,14 @@ namespace osu.Game.Overlays
protected override void PopIn() protected override void PopIn()
{ {
base.PopIn(); base.PopIn();
FadeEdgeEffectTo(0.25f, APPEAR_DURATION, Easing.In); FadeEdgeEffectTo(0.25f, WaveContainer.APPEAR_DURATION, Easing.In);
} }
protected override void PopOut() protected override void PopOut()
{ {
base.PopOut(); base.PopOut();
header.Details.StopPreview(); header.Details.StopPreview();
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out); FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out);
} }
protected override bool OnClick(InputState state) protected override bool OnClick(InputState state)
@ -139,7 +116,7 @@ namespace osu.Game.Overlays
return true; return true;
} }
public void ShowBeatmapSet(int beatmapSetId) public void FetchAndShowBeatmapSet(int beatmapSetId)
{ {
// todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work. // todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work.
var req = new GetBeatmapSetRequest(beatmapSetId); var req = new GetBeatmapSetRequest(beatmapSetId);

View File

@ -11,7 +11,9 @@ using osu.Framework.Localisation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
namespace osu.Game.Overlays.Direct namespace osu.Game.Overlays.Direct
{ {
@ -20,7 +22,7 @@ namespace osu.Game.Overlays.Direct
private const float horizontal_padding = 10; private const float horizontal_padding = 10;
private const float vertical_padding = 5; private const float vertical_padding = 5;
private FillFlowContainer bottomPanel; private FillFlowContainer bottomPanel, statusContainer;
private PlayButton playButton; private PlayButton playButton;
private Box progressBar; private Box progressBar;
@ -199,7 +201,37 @@ namespace osu.Game.Overlays.Direct
Size = new Vector2(30), Size = new Vector2(30),
Alpha = 0, Alpha = 0,
}, },
statusContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = 5, Left = 5 },
Spacing = new Vector2(5),
},
}); });
if (SetInfo.OnlineInfo?.HasVideo ?? false)
{
statusContainer.Add(new IconPill(FontAwesome.fa_film));
}
statusContainer.Add(new BeatmapSetOnlineStatusPill(12, new MarginPadding { Horizontal = 10, Vertical = 5 })
{
Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None,
});
}
protected override bool OnHover(InputState state)
{
statusContainer.FadeOut(120, Easing.InOutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
base.OnHoverLost(state);
statusContainer.FadeIn(120, Easing.InOutQuint);
} }
} }
} }

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using OpenTK; using OpenTK;
@ -208,7 +209,7 @@ namespace osu.Game.Overlays.Direct
{ {
var icons = new List<DifficultyIcon>(); var icons = new List<DifficultyIcon>();
foreach (var b in SetInfo.Beatmaps) foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty))
icons.Add(new DifficultyIcon(b)); icons.Add(new DifficultyIcon(b));
return icons; return icons;

View File

@ -7,15 +7,15 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.SearchableList; using osu.Game.Overlays.SearchableList;
using osu.Game.Rulesets; using osu.Game.Rulesets;
namespace osu.Game.Overlays.Direct namespace osu.Game.Overlays.Direct
{ {
public class FilterControl : SearchableListFilterControl<DirectSortCriteria, RankStatus> public class FilterControl : SearchableListFilterControl<DirectSortCriteria, BeatmapSearchCategory>
{ {
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>(); public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
private FillFlowContainer<RulesetToggleButton> modeButtons; private FillFlowContainer<RulesetToggleButton> modeButtons;

View File

@ -13,7 +13,6 @@ namespace osu.Game.Overlays.Direct
public class Header : SearchableListHeader<DirectTab> public class Header : SearchableListHeader<DirectTab>
{ {
protected override Color4 BackgroundColour => OsuColour.FromHex(@"252f3a"); protected override Color4 BackgroundColour => OsuColour.FromHex(@"252f3a");
protected override float TabStripWidth => 298;
protected override DirectTab DefaultTab => DirectTab.Search; protected override DirectTab DefaultTab => DirectTab.Search;
protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", TextSize = 25 }; protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", TextSize = 25 };

View File

@ -0,0 +1,43 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Direct
{
public class IconPill : CircularContainer
{
public IconPill(FontAwesome icon)
{
AutoSizeAxes = Axes.Both;
Masking = true;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new Container
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding(5),
Child = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = icon,
Size = new Vector2(12),
},
},
};
}
}
}

View File

@ -78,12 +78,7 @@ namespace osu.Game.Overlays.Direct
loadingAnimation = new LoadingAnimation(), loadingAnimation = new LoadingAnimation(),
}); });
Playing.ValueChanged += playing => Playing.ValueChanged += updatePreviewTrack;
{
icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
updatePreviewTrack(playing);
};
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -125,6 +120,15 @@ namespace osu.Game.Overlays.Direct
private void updatePreviewTrack(bool playing) private void updatePreviewTrack(bool playing)
{ {
if (playing && BeatmapSet == null)
{
Playing.Value = false;
return;
}
icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
if (playing) if (playing)
{ {
if (Preview == null) if (Preview == null)
@ -173,8 +177,9 @@ namespace osu.Game.Overlays.Direct
if (trackLoader != d) return; if (trackLoader != d) return;
Preview = d?.Preview; Preview = d?.Preview;
Playing.TriggerChange(); updatePreviewTrack(Playing);
loading = false; loading = false;
Add(trackLoader); Add(trackLoader);
}); });
} }

View File

@ -22,7 +22,7 @@ using OpenTK.Graphics;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
public class DirectOverlay : SearchableListOverlay<DirectTab, DirectSortCriteria, RankStatus> public class DirectOverlay : SearchableListOverlay<DirectTab, DirectSortCriteria, BeatmapSearchCategory>
{ {
private const float panel_padding = 10f; private const float panel_padding = 10f;
@ -40,7 +40,7 @@ namespace osu.Game.Overlays
protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265"); protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265");
protected override SearchableListHeader<DirectTab> CreateHeader() => new Header(); protected override SearchableListHeader<DirectTab> CreateHeader() => new Header();
protected override SearchableListFilterControl<DirectSortCriteria, RankStatus> CreateFilterControl() => new FilterControl(); protected override SearchableListFilterControl<DirectSortCriteria, BeatmapSearchCategory> CreateFilterControl() => new FilterControl();
private IEnumerable<BeatmapSetInfo> beatmapSets; private IEnumerable<BeatmapSetInfo> beatmapSets;
@ -89,10 +89,10 @@ namespace osu.Game.Overlays
// osu!direct colours are not part of the standard palette // osu!direct colours are not part of the standard palette
FirstWaveColour = OsuColour.FromHex(@"19b0e2"); Waves.FirstWaveColour = OsuColour.FromHex(@"19b0e2");
SecondWaveColour = OsuColour.FromHex(@"2280a2"); Waves.SecondWaveColour = OsuColour.FromHex(@"2280a2");
ThirdWaveColour = OsuColour.FromHex(@"005774"); Waves.ThirdWaveColour = OsuColour.FromHex(@"005774");
FourthWaveColour = OsuColour.FromHex(@"003a4e"); Waves.FourthWaveColour = OsuColour.FromHex(@"003a4e");
ScrollFlow.Children = new Drawable[] ScrollFlow.Children = new Drawable[]
{ {
@ -188,12 +188,12 @@ namespace osu.Game.Overlays
beatmaps.ItemAdded += setAdded; beatmaps.ItemAdded += setAdded;
} }
private void setAdded(BeatmapSetInfo set) private void setAdded(BeatmapSetInfo set) => Schedule(() =>
{ {
// if a new map was imported, we should remove it from search results (download completed etc.) // if a new map was imported, we should remove it from search results (download completed etc.)
panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire(); panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire();
BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID); BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID);
} });
private void updateResultCounts() private void updateResultCounts()
{ {
@ -323,6 +323,14 @@ namespace osu.Game.Overlays
private int distinctCount(List<string> list) => list.Distinct().ToArray().Length; private int distinctCount(List<string> list) => list.Distinct().ToArray().Length;
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (beatmaps != null)
beatmaps.ItemAdded -= setAdded;
}
public class ResultCounts public class ResultCounts
{ {
public readonly int Artists; public readonly int Artists;

View File

@ -47,7 +47,7 @@ namespace osu.Game.Overlays.KeyBinding
private FillFlowContainer<KeyButton> buttons; private FillFlowContainer<KeyButton> buttons;
public IEnumerable<string> FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())); public IEnumerable<string> FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text);
public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings) public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings)
{ {

View File

@ -18,6 +18,7 @@ using System.Linq;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -113,14 +114,14 @@ namespace osu.Game.Overlays.Mods
{ {
base.PopOut(); base.PopOut();
footerContainer.MoveToX(footerContainer.DrawSize.X, DISAPPEAR_DURATION, Easing.InSine); footerContainer.MoveToX(footerContainer.DrawSize.X, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
footerContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine); footerContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
foreach (ModSection section in ModSectionsContainer.Children) foreach (ModSection section in ModSectionsContainer.Children)
{ {
section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), DISAPPEAR_DURATION, Easing.InSine); section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.MoveToX(100f, DISAPPEAR_DURATION, Easing.InSine); section.ButtonsContainer.MoveToX(100f, WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine); section.ButtonsContainer.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.InSine);
} }
} }
@ -128,14 +129,14 @@ namespace osu.Game.Overlays.Mods
{ {
base.PopIn(); base.PopIn();
footerContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint); footerContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
footerContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint); footerContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
foreach (ModSection section in ModSectionsContainer.Children) foreach (ModSection section in ModSectionsContainer.Children)
{ {
section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), APPEAR_DURATION, Easing.OutQuint); section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), WaveContainer.APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint); section.ButtonsContainer.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint); section.ButtonsContainer.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint);
} }
} }
@ -181,14 +182,12 @@ namespace osu.Game.Overlays.Mods
public ModSelectOverlay() public ModSelectOverlay()
{ {
FirstWaveColour = OsuColour.FromHex(@"19b0e2"); Waves.FirstWaveColour = OsuColour.FromHex(@"19b0e2");
SecondWaveColour = OsuColour.FromHex(@"2280a2"); Waves.SecondWaveColour = OsuColour.FromHex(@"2280a2");
ThirdWaveColour = OsuColour.FromHex(@"005774"); Waves.ThirdWaveColour = OsuColour.FromHex(@"005774");
FourthWaveColour = OsuColour.FromHex(@"003a4e"); Waves.FourthWaveColour = OsuColour.FromHex(@"003a4e");
Height = 510; Height = 510;
Content.RelativeSizeAxes = Axes.X;
Content.AutoSizeAxes = Axes.Y;
Children = new Drawable[] Children = new Drawable[]
{ {
new Container new Container

View File

@ -74,8 +74,8 @@ namespace osu.Game.Overlays.Music
}, },
}; };
beatmaps.ItemAdded += list.AddBeatmapSet; beatmaps.ItemAdded += handleBeatmapAdded;
beatmaps.ItemRemoved += list.RemoveBeatmapSet; beatmaps.ItemRemoved += handleBeatmapRemoved;
list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets();
@ -95,6 +95,9 @@ namespace osu.Game.Overlays.Music
beatmapBacking.TriggerChange(); beatmapBacking.TriggerChange();
} }
private void handleBeatmapAdded(BeatmapSetInfo setInfo) => Schedule(() => list.AddBeatmapSet(setInfo));
private void handleBeatmapRemoved(BeatmapSetInfo setInfo) => Schedule(() => list.RemoveBeatmapSet(setInfo));
protected override void PopIn() protected override void PopIn()
{ {
filter.Search.HoldFocus = true; filter.Search.HoldFocus = true;
@ -153,6 +156,17 @@ namespace osu.Game.Overlays.Music
track.Restart(); track.Restart();
} }
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (beatmaps != null)
{
beatmaps.ItemAdded -= handleBeatmapAdded;
beatmaps.ItemRemoved -= handleBeatmapRemoved;
}
}
} }
//todo: placeholder //todo: placeholder

View File

@ -0,0 +1,195 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using OpenTK;
namespace osu.Game.Overlays.Profile.Header
{
public class BadgeContainer : Container
{
private static readonly Vector2 badge_size = new Vector2(86, 40);
private static readonly MarginPadding outer_padding = new MarginPadding(3);
private OsuSpriteText badgeCountText;
private FillFlowContainer badgeFlowContainer;
private FillFlowContainer outerBadgeContainer;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Child = new Container
{
Masking = true,
CornerRadius = 4,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray3
},
outerBadgeContainer = new OuterBadgeContainer(onOuterHover, onOuterHoverLost)
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Direction = FillDirection.Vertical,
Padding = outer_padding,
Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
badgeCountText = new OsuSpriteText
{
Alpha = 0,
TextSize = 12,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Font = "Exo2.0-Regular"
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Child = badgeFlowContainer = new FillFlowContainer
{
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
}
}
}
},
}
};
Scheduler.AddDelayed(rotateBadges, 3000, true);
}
private void rotateBadges()
{
if (outerBadgeContainer.IsHovered) return;
visibleBadge = (visibleBadge + 1) % badgeCount;
badgeFlowContainer.MoveToX(-DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge, 500, Easing.InOutQuad);
}
private int visibleBadge;
private int badgeCount;
public void ShowBadges(Badge[] badges)
{
switch (badges.Length)
{
case 0:
Hide();
return;
case 1:
badgeCountText.Hide();
break;
default:
badgeCountText.Show();
badgeCountText.Text = $"{badges.Length} badges";
break;
}
Show();
badgeCount = badges.Length;
visibleBadge = 0;
badgeFlowContainer.Clear();
foreach (var badge in badges)
{
LoadComponentAsync(new DrawableBadge(badge)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
}, badgeFlowContainer.Add);
}
}
private void onOuterHover()
{
badgeFlowContainer.ClearTransforms();
badgeFlowContainer.X = 0;
badgeFlowContainer.Direction = FillDirection.Full;
outerBadgeContainer.AutoSizeAxes = Axes.Both;
badgeFlowContainer.MaximumSize = new Vector2(ChildSize.X, float.MaxValue);
}
private void onOuterHoverLost()
{
badgeFlowContainer.X = -DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge;
badgeFlowContainer.Direction = FillDirection.Horizontal;
outerBadgeContainer.AutoSizeAxes = Axes.Y;
outerBadgeContainer.Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal;
}
private class OuterBadgeContainer : FillFlowContainer
{
private readonly Action hoverAction;
private readonly Action hoverLostAction;
public OuterBadgeContainer(Action hoverAction, Action hoverLostAction)
{
this.hoverAction = hoverAction;
this.hoverLostAction = hoverLostAction;
}
protected override bool OnHover(InputState state)
{
hoverAction();
return true;
}
protected override void OnHoverLost(InputState state) => hoverLostAction();
}
private class DrawableBadge : Container, IHasTooltip
{
public static readonly Vector2 DRAWABLE_BADGE_SIZE = badge_size + outer_padding.Total;
private readonly Badge badge;
public DrawableBadge(Badge badge)
{
this.badge = badge;
Padding = outer_padding;
Size = DRAWABLE_BADGE_SIZE;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Child = new Sprite
{
FillMode = FillMode.Fit,
RelativeSizeAxes = Axes.Both,
Texture = textures.Get(badge.ImageUrl),
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Child.FadeInFromZero(200);
}
public string TooltipText => badge.Description;
}
}
}

View File

@ -2,9 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenTK;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration;
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;
@ -14,10 +15,9 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Users; using osu.Game.Users;
using System.Collections.Generic; using OpenTK;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.Profile namespace osu.Game.Overlays.Profile.Header
{ {
public class RankGraph : Container public class RankGraph : Container
{ {

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
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;
@ -9,8 +8,9 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using OpenTK;
namespace osu.Game.Overlays.Profile namespace osu.Game.Overlays.Profile.Header
{ {
public class SupporterIcon : CircularContainer, IHasTooltip public class SupporterIcon : CircularContainer, IHasTooltip
{ {

Some files were not shown because too many files have changed in this diff Show More