1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-21 03:02:54 +08:00

Merge remote-tracking branch 'upstream/master' into tgi74-ctb_hr_mangling

This commit is contained in:
Dean Herbert 2018-04-18 16:46:02 +09:00
commit d307b8dc2e
1083 changed files with 96135 additions and 95337 deletions

40
.gitattributes vendored
View File

@ -1,19 +1,23 @@
# 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
*.tga binary # Check out with lf (UNIX) line endings
*.ttf binary *.sh text eol=lf
*.wav binary .gitignore text eol=lf
*.xnb binary .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>

@ -1 +1 @@
Subproject commit 02d7a0fa4798d197cd08570ee48951edbb7c7860 Subproject commit 16e6a453db9a8f4454238a2911eb5f1444b7ec2a

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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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,7 @@ 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);
} }
public OsuConfigManager(Storage storage) : base(storage) public OsuConfigManager(Storage storage) : base(storage)
@ -128,6 +129,7 @@ namespace osu.Game.Configuration
ShowConvertedBeatmaps, ShowConvertedBeatmaps,
SpeedChangeVisualisation, SpeedChangeVisualisation,
Skin, Skin,
ScreenshotFormat ScreenshotFormat,
ScreenshotCaptureMenuCursor
} }
} }

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

@ -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

@ -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

@ -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
@ -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

@ -44,7 +44,7 @@ namespace osu.Game.Overlays.BeatmapSet
Beatmap.Value = BeatmapSet.Beatmaps.First(); Beatmap.Value = BeatmapSet.Beatmaps.First();
plays.Value = BeatmapSet.OnlineInfo.PlayCount; plays.Value = BeatmapSet.OnlineInfo.PlayCount;
favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount; favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount;
difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.Select(b => new DifficultySelectorButton(b) difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b)
{ {
State = DifficultySelectorState.NotSelected, State = DifficultySelectorState.NotSelected,
OnHovered = beatmap => OnHovered = beatmap =>

View File

@ -246,11 +246,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

@ -40,10 +40,10 @@ namespace osu.Game.Overlays
public BeatmapSetOverlay() public BeatmapSetOverlay()
{ {
FirstWaveColour = OsuColour.Gray(0.4f); Waves.FirstWaveColour = OsuColour.Gray(0.4f);
SecondWaveColour = OsuColour.Gray(0.3f); Waves.SecondWaveColour = OsuColour.Gray(0.3f);
ThirdWaveColour = OsuColour.Gray(0.2f); Waves.ThirdWaveColour = OsuColour.Gray(0.2f);
FourthWaveColour = OsuColour.Gray(0.1f); Waves.FourthWaveColour = OsuColour.Gray(0.1f);
Anchor = Anchor.TopCentre; Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;
@ -123,14 +123,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)

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

@ -173,8 +173,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

@ -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
{ {

View File

@ -17,6 +17,7 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Profile.Header;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Overlays.Profile namespace osu.Game.Overlays.Profile
@ -35,6 +36,7 @@ namespace osu.Game.Overlays.Profile
private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA; private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA;
private readonly Box colourBar; private readonly Box colourBar;
private readonly DrawableFlag countryFlag; private readonly DrawableFlag countryFlag;
private readonly BadgeContainer badgeContainer;
private const float cover_height = 350; private const float cover_height = 350;
private const float info_height = 150; private const float info_height = 150;
@ -42,6 +44,7 @@ namespace osu.Game.Overlays.Profile
private const float avatar_size = 110; private const float avatar_size = 110;
private const float level_position = 30; private const float level_position = 30;
private const float level_height = 60; private const float level_height = 60;
private const float stats_width = 280;
public ProfileHeader(User user) public ProfileHeader(User user)
{ {
@ -66,9 +69,9 @@ namespace osu.Game.Overlays.Profile
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
X = UserProfileOverlay.CONTENT_X_MARGIN, Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN, Bottom = 20, Right = stats_width + UserProfileOverlay.CONTENT_X_MARGIN },
Y = -20, AutoSizeAxes = Axes.Y,
AutoSizeAxes = Axes.Both, RelativeSizeAxes = Axes.X,
Children = new Drawable[] Children = new Drawable[]
{ {
new UpdateableAvatar new UpdateableAvatar
@ -116,7 +119,15 @@ namespace osu.Game.Overlays.Profile
Height = 20 Height = 20
} }
} }
} },
badgeContainer = new BadgeContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Bottom = 5 },
Alpha = 0,
},
} }
}, },
colourBar = new Box colourBar = new Box
@ -156,7 +167,7 @@ namespace osu.Game.Overlays.Profile
{ {
X = -UserProfileOverlay.CONTENT_X_MARGIN, X = -UserProfileOverlay.CONTENT_X_MARGIN,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Width = 280, Width = stats_width,
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
Children = new Drawable[] Children = new Drawable[]
@ -417,6 +428,8 @@ namespace osu.Game.Overlays.Profile
rankGraph.User.Value = user; rankGraph.User.Value = user;
} }
badgeContainer.ShowBadges(user.Badges);
} }
private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null) private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null)

View File

@ -1,6 +1,7 @@
// 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 System;
using OpenTK; using OpenTK;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -19,6 +20,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
private DirectPanel currentlyPlaying; private DirectPanel currentlyPlaying;
public event Action<PaginatedBeatmapContainer> BeganPlayingPreview;
public PaginatedBeatmapContainer(BeatmapSetType type, Bindable<User> user, string header, string missing = "None... yet.") public PaginatedBeatmapContainer(BeatmapSetType type, Bindable<User> user, string header, string missing = "None... yet.")
: base(user, header, missing) : base(user, header, missing)
{ {
@ -56,17 +59,25 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
panel.PreviewPlaying.ValueChanged += isPlaying => panel.PreviewPlaying.ValueChanged += isPlaying =>
{ {
if (!isPlaying) return; StopPlayingPreview();
if (currentlyPlaying != null && currentlyPlaying != panel)
currentlyPlaying.PreviewPlaying.Value = false;
if (isPlaying)
{
BeganPlayingPreview?.Invoke(this);
currentlyPlaying = panel; currentlyPlaying = panel;
}
}; };
} }
}; };
Api.Queue(req); Api.Queue(req);
} }
public void StopPlayingPreview()
{
if (currentlyPlaying == null) return;
currentlyPlaying.PreviewPlaying.Value = false;
currentlyPlaying = null;
}
} }
} }

View File

@ -1,6 +1,7 @@
// 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 System.Linq;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections.Beatmaps; using osu.Game.Overlays.Profile.Sections.Beatmaps;
@ -21,6 +22,15 @@ namespace osu.Game.Overlays.Profile.Sections
new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"), new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"),
new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"), new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"),
}; };
foreach (var paginatedBeatmapContainer in Children.OfType<PaginatedBeatmapContainer>())
{
paginatedBeatmapContainer.BeganPlayingPreview += _ =>
{
foreach (var bc in Children.OfType<PaginatedBeatmapContainer>())
bc.StopPlayingPreview();
};
}
} }
} }
} }

View File

@ -30,6 +30,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{ {
LabelText = "Screenshot format", LabelText = "Screenshot format",
Bindable = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat) Bindable = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat)
},
new SettingsCheckbox
{
LabelText = "Show menu cursor in screenshots",
Bindable = config.GetBindable<bool>(OsuSetting.ScreenshotCaptureMenuCursor)
} }
}; };
} }

View File

@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
protected override string Header => "Mouse"; protected override string Header => "Mouse";
private readonly BindableBool rawInputToggle = new BindableBool(); private readonly BindableBool rawInputToggle = new BindableBool();
private Bindable<string> activeInputHandlers; private Bindable<string> ignoredInputHandler;
private SensitivitySetting sensitivity; private SensitivitySetting sensitivity;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -61,20 +61,18 @@ namespace osu.Game.Overlays.Settings.Sections.Input
const string raw_mouse_handler = @"OpenTKRawMouseHandler"; const string raw_mouse_handler = @"OpenTKRawMouseHandler";
const string standard_mouse_handler = @"OpenTKMouseHandler"; const string standard_mouse_handler = @"OpenTKMouseHandler";
activeInputHandlers.Value = enabled ? ignoredInputHandler.Value = enabled ? standard_mouse_handler : raw_mouse_handler;
activeInputHandlers.Value.Replace(standard_mouse_handler, raw_mouse_handler) :
activeInputHandlers.Value.Replace(raw_mouse_handler, standard_mouse_handler);
}; };
activeInputHandlers = config.GetBindable<string>(FrameworkSetting.ActiveInputHandlers); ignoredInputHandler = config.GetBindable<string>(FrameworkSetting.IgnoredInputHandlers);
activeInputHandlers.ValueChanged += handlers => ignoredInputHandler.ValueChanged += handler =>
{ {
bool raw = handlers.Contains("Raw"); bool raw = !handler.Contains("Raw");
rawInputToggle.Value = raw; rawInputToggle.Value = raw;
sensitivity.Bindable.Disabled = !raw; sensitivity.Bindable.Disabled = !raw;
}; };
activeInputHandlers.TriggerChange(); ignoredInputHandler.TriggerChange();
} }
private class SensitivitySetting : SettingsSlider<double, SensitivitySlider> private class SensitivitySetting : SettingsSlider<double, SensitivitySlider>

View File

@ -21,9 +21,13 @@ namespace osu.Game.Overlays.Settings.Sections
public override FontAwesome Icon => FontAwesome.fa_paint_brush; public override FontAwesome Icon => FontAwesome.fa_paint_brush;
private SkinManager skins;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config, SkinManager skins) private void load(OsuConfigManager config, SkinManager skins)
{ {
this.skins = skins;
FlowContent.Spacing = new Vector2(0, 5); FlowContent.Spacing = new Vector2(0, 5);
Children = new Drawable[] Children = new Drawable[]
{ {
@ -47,15 +51,29 @@ namespace osu.Game.Overlays.Settings.Sections
}, },
}; };
void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair<string, int>(s.ToString(), s.ID)); skins.ItemAdded += onItemsChanged;
skins.ItemAdded += _ => reloadSkins(); skins.ItemRemoved += onItemsChanged;
skins.ItemRemoved += _ => reloadSkins();
reloadSkins(); reloadSkins();
skinDropdown.Bindable = config.GetBindable<int>(OsuSetting.Skin); skinDropdown.Bindable = config.GetBindable<int>(OsuSetting.Skin);
} }
private void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair<string, int>(s.ToString(), s.ID));
private void onItemsChanged(SkinInfo _) => Schedule(reloadSkins);
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (skins != null)
{
skins.ItemAdded -= onItemsChanged;
skins.ItemRemoved -= onItemsChanged;
}
}
private class SizeSlider : OsuSliderBar<double> private class SizeSlider : OsuSliderBar<double>
{ {
public override string TooltipText => Current.Value.ToString(@"0.##x"); public override string TooltipText => Current.Value.ToString(@"0.##x");

View File

@ -48,10 +48,10 @@ namespace osu.Game.Overlays
public SocialOverlay() public SocialOverlay()
{ {
FirstWaveColour = OsuColour.FromHex(@"cb5fa0"); Waves.FirstWaveColour = OsuColour.FromHex(@"cb5fa0");
SecondWaveColour = OsuColour.FromHex(@"b04384"); Waves.SecondWaveColour = OsuColour.FromHex(@"b04384");
ThirdWaveColour = OsuColour.FromHex(@"9b2b6e"); Waves.ThirdWaveColour = OsuColour.FromHex(@"9b2b6e");
FourthWaveColour = OsuColour.FromHex(@"6d214d"); Waves.FourthWaveColour = OsuColour.FromHex(@"6d214d");
Add(loading = new LoadingAnimation()); Add(loading = new LoadingAnimation());

View File

@ -35,10 +35,10 @@ namespace osu.Game.Overlays
public UserProfileOverlay() public UserProfileOverlay()
{ {
FirstWaveColour = OsuColour.Gray(0.4f); Waves.FirstWaveColour = OsuColour.Gray(0.4f);
SecondWaveColour = OsuColour.Gray(0.3f); Waves.SecondWaveColour = OsuColour.Gray(0.3f);
ThirdWaveColour = OsuColour.Gray(0.2f); Waves.ThirdWaveColour = OsuColour.Gray(0.2f);
FourthWaveColour = OsuColour.Gray(0.1f); Waves.FourthWaveColour = OsuColour.Gray(0.1f);
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
RelativePositionAxes = Axes.Both; RelativePositionAxes = Axes.Both;
@ -64,13 +64,13 @@ namespace osu.Game.Overlays
protected override void PopIn() protected override void PopIn()
{ {
base.PopIn(); base.PopIn();
FadeEdgeEffectTo(0.5f, APPEAR_DURATION, Easing.In); FadeEdgeEffectTo(0.5f, WaveContainer.APPEAR_DURATION, Easing.In);
} }
protected override void PopOut() protected override void PopOut()
{ {
base.PopOut(); base.PopOut();
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out); FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out);
} }
public void ShowUser(long userId) public void ShowUser(long userId)

View File

@ -1,203 +1,37 @@
// 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.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using System;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
public abstract class WaveOverlayContainer : OsuFocusedOverlayContainer public abstract class WaveOverlayContainer : OsuFocusedOverlayContainer
{ {
protected const float APPEAR_DURATION = 800; protected readonly WaveContainer Waves;
protected 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 bool BlockPassThroughKeyboard => true; protected override bool BlockPassThroughKeyboard => true;
protected override Container<Drawable> Content => Waves;
protected override Container<Drawable> Content => contentContainer;
protected Color4 FirstWaveColour
{
get
{
return firstWave.Colour;
}
set
{
if (firstWave.Colour == value) return;
firstWave.Colour = value;
}
}
protected Color4 SecondWaveColour
{
get
{
return secondWave.Colour;
}
set
{
if (secondWave.Colour == value) return;
secondWave.Colour = value;
}
}
protected Color4 ThirdWaveColour
{
get
{
return thirdWave.Colour;
}
set
{
if (thirdWave.Colour == value) return;
thirdWave.Colour = value;
}
}
protected Color4 FourthWaveColour
{
get
{
return fourthWave.Colour;
}
set
{
if (fourthWave.Colour == value) return;
fourthWave.Colour = value;
}
}
protected WaveOverlayContainer() protected WaveOverlayContainer()
{ {
Masking = true; AddInternal(Waves = new WaveContainer
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, RelativeSizeAxes = Axes.Both,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
}); });
} }
protected override void PopIn() protected override void PopIn()
{ {
base.PopIn(); base.PopIn();
Waves.Show();
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() protected override void PopOut()
{ {
base.PopOut(); base.PopOut();
Waves.Hide();
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

@ -4,20 +4,16 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation; 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.Framework.Input;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.MathUtils;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Screens.Compose;
using osu.Game.Screens.Edit.Screens.Compose.Layers; using osu.Game.Screens.Edit.Screens.Compose.Layers;
using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; using osu.Game.Screens.Edit.Screens.Compose.RadioButtons;
@ -33,9 +29,6 @@ namespace osu.Game.Rulesets.Edit
private readonly List<Container> layerContainers = new List<Container>(); private readonly List<Container> layerContainers = new List<Container>();
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>(); private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
private IAdjustableClock adjustableClock;
protected HitObjectComposer(Ruleset ruleset) protected HitObjectComposer(Ruleset ruleset)
{ {
@ -44,14 +37,9 @@ namespace osu.Game.Rulesets.Edit
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader]
private void load([NotNull] OsuGameBase osuGame, [NotNull] IAdjustableClock adjustableClock, [NotNull] IFrameBasedClock framedClock, [CanBeNull] BindableBeatDivisor beatDivisor) private void load(OsuGameBase osuGame, IFrameBasedClock framedClock)
{ {
this.adjustableClock = adjustableClock;
if (beatDivisor != null)
this.beatDivisor.BindTo(beatDivisor);
beatmap.BindTo(osuGame.Beatmap); beatmap.BindTo(osuGame.Beatmap);
try try
@ -135,106 +123,6 @@ namespace osu.Game.Rulesets.Edit
}); });
} }
protected override bool OnWheel(InputState state)
{
if (state.Mouse.WheelDelta > 0)
SeekBackward(true);
else
SeekForward(true);
return true;
}
/// <summary>
/// Seeks the current time one beat-snapped beat-length backwards.
/// </summary>
/// <param name="snapped">Whether to snap to the closest beat.</param>
public void SeekBackward(bool snapped = false) => seek(-1, snapped);
/// <summary>
/// Seeks the current time one beat-snapped beat-length forwards.
/// </summary>
/// <param name="snapped">Whether to snap to the closest beat.</param>
public void SeekForward(bool snapped = false) => seek(1, snapped);
private void seek(int direction, bool snapped)
{
var cpi = beatmap.Value.Beatmap.ControlPointInfo;
var timingPoint = cpi.TimingPointAt(adjustableClock.CurrentTime);
if (direction < 0 && timingPoint.Time == adjustableClock.CurrentTime)
{
// When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into
int activeIndex = cpi.TimingPoints.IndexOf(timingPoint);
while (activeIndex > 0 && adjustableClock.CurrentTime == timingPoint.Time)
timingPoint = cpi.TimingPoints[--activeIndex];
}
double seekAmount = timingPoint.BeatLength / beatDivisor;
double seekTime = adjustableClock.CurrentTime + seekAmount * direction;
if (!snapped || cpi.TimingPoints.Count == 0)
{
adjustableClock.Seek(seekTime);
return;
}
// We will be snapping to beats within timingPoint
seekTime -= timingPoint.Time;
// Determine the index from timingPoint of the closest beat to seekTime, accounting for scrolling direction
int closestBeat;
if (direction > 0)
closestBeat = (int)Math.Floor(seekTime / seekAmount);
else
closestBeat = (int)Math.Ceiling(seekTime / seekAmount);
seekTime = timingPoint.Time + closestBeat * seekAmount;
// Due to the rounding above, we may end up on the current beat. This will effectively cause 0 seeking to happen, but we don't want this.
// Instead, we'll go to the next beat in the direction when this is the case
if (Precision.AlmostEquals(adjustableClock.CurrentTime, seekTime))
{
closestBeat += direction > 0 ? 1 : -1;
seekTime = timingPoint.Time + closestBeat * seekAmount;
}
if (seekTime < timingPoint.Time && timingPoint != cpi.TimingPoints.First())
seekTime = timingPoint.Time;
var nextTimingPoint = cpi.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time);
if (seekTime > nextTimingPoint?.Time)
seekTime = nextTimingPoint.Time;
adjustableClock.Seek(seekTime);
}
public void SeekTo(double seekTime, bool snapped = false)
{
if (!snapped)
{
adjustableClock.Seek(seekTime);
return;
}
var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(seekTime);
double beatSnapLength = timingPoint.BeatLength / beatDivisor;
// We will be snapping to beats within the timing point
seekTime -= timingPoint.Time;
// Determine the index from the current timing point of the closest beat to seekTime
int closestBeat = (int)Math.Round(seekTime / beatSnapLength);
seekTime = timingPoint.Time + closestBeat * beatSnapLength;
// Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to
// the next timing point's start time
var nextTimingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time);
if (seekTime > nextTimingPoint?.Time)
seekTime = nextTimingPoint.Time;
adjustableClock.Seek(seekTime);
}
private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool;
protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true);

View File

@ -14,6 +14,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets namespace osu.Game.Rulesets
{ {
@ -33,6 +34,13 @@ namespace osu.Game.Rulesets
public abstract IEnumerable<Mod> GetModsFor(ModType type); public abstract IEnumerable<Mod> GetModsFor(ModType type);
/// <summary>
/// Converts mods from legacy enum values. Do not override if you're not a legacy ruleset.
/// </summary>
/// <param name="mods">The legacy enum which will be converted</param>
/// <returns>An enumerable of constructed <see cref="Mod"/>s</returns>
public virtual IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods) => new Mod[] { };
public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay); public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay);
protected Ruleset(RulesetInfo rulesetInfo = null) protected Ruleset(RulesetInfo rulesetInfo = null)

View File

@ -9,6 +9,8 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Replays.Legacy; using osu.Game.Rulesets.Replays.Legacy;
using osu.Game.Users; using osu.Game.Users;
using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.LZMA;
using osu.Game.Beatmaps.Legacy;
using System.Linq;
namespace osu.Game.Rulesets.Scoring.Legacy namespace osu.Game.Rulesets.Scoring.Legacy
{ {
@ -64,7 +66,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy
/* score.Perfect = */ /* score.Perfect = */
sr.ReadBoolean(); sr.ReadBoolean();
/* score.EnabledMods = (Mods)*/ /* score.EnabledMods = (Mods)*/
sr.ReadInt32(); score.Mods = currentRuleset.ConvertLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
/* score.HpGraphString = */ /* score.HpGraphString = */
sr.ReadString(); sr.ReadString();
/* score.Date = */ /* score.Date = */

View File

@ -55,11 +55,9 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
if (Beatmap.Value.Track.Length == double.PositiveInfinity) return; if (Beatmap.Value.Track.Length == double.PositiveInfinity) return;
float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth);
seekTo(markerPos / DrawWidth * Beatmap.Value.Track.Length); adjustableClock.Seek(markerPos / DrawWidth * Beatmap.Value.Track.Length);
} }
private void seekTo(double time) => adjustableClock.Seek(time);
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();

View File

@ -12,6 +12,7 @@ using osu.Game.Screens.Edit.Menus;
using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Screens; using osu.Game.Screens.Edit.Screens;
@ -32,6 +33,10 @@ namespace osu.Game.Screens.Edit
private EditorScreen currentScreen; private EditorScreen currentScreen;
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
private EditorClock clock;
private DependencyContainer dependencies; private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
@ -42,11 +47,12 @@ namespace osu.Game.Screens.Edit
{ {
// TODO: should probably be done at a RulesetContainer level to share logic with Player. // TODO: should probably be done at a RulesetContainer level to share logic with Player.
var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock(); var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock();
var adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; clock = new EditorClock(Beatmap.Value.Beatmap.ControlPointInfo, beatDivisor) { IsCoupled = false };
adjustableClock.ChangeSource(sourceClock); clock.ChangeSource(sourceClock);
dependencies.CacheAs<IAdjustableClock>(adjustableClock); dependencies.CacheAs<IFrameBasedClock>(clock);
dependencies.CacheAs<IFrameBasedClock>(adjustableClock); dependencies.CacheAs<IAdjustableClock>(clock);
dependencies.Cache(beatDivisor);
EditorMenuBar menuBar; EditorMenuBar menuBar;
TimeInfoContainer timeInfo; TimeInfoContainer timeInfo;
@ -147,7 +153,6 @@ namespace osu.Game.Screens.Edit
menuBar.Mode.ValueChanged += onModeChanged; menuBar.Mode.ValueChanged += onModeChanged;
bottomBackground.Colour = colours.Gray2; bottomBackground.Colour = colours.Gray2;
} }
private void exportBeatmap() private void exportBeatmap()
@ -176,6 +181,15 @@ namespace osu.Game.Screens.Edit
screenContainer.Add(currentScreen); screenContainer.Add(currentScreen);
} }
protected override bool OnWheel(InputState state)
{
if (state.Mouse.WheelDelta > 0)
clock.SeekBackward(true);
else
clock.SeekForward(true);
return true;
}
protected override void OnResuming(Screen last) protected override void OnResuming(Screen last)
{ {
Beatmap.Value.Track?.Stop(); Beatmap.Value.Track?.Stop();

View File

@ -0,0 +1,117 @@
// 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.Linq;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Screens.Edit.Screens.Compose;
namespace osu.Game.Screens.Edit
{
/// <summary>
/// A decoupled clock which adds editor-specific functionality, such as snapping to a user-defined beat divisor.
/// </summary>
public class EditorClock : DecoupleableInterpolatingFramedClock
{
public ControlPointInfo ControlPointInfo;
private readonly BindableBeatDivisor beatDivisor;
public EditorClock(ControlPointInfo controlPointInfo, BindableBeatDivisor beatDivisor)
{
this.beatDivisor = beatDivisor;
ControlPointInfo = controlPointInfo;
}
/// <summary>
/// Seek to the closest snappable beat from a time.
/// </summary>
/// <param name="position">The raw position which should be seeked around.</param>
/// <returns>Whether the seek could be performed.</returns>
public bool SeekSnapped(double position)
{
var timingPoint = ControlPointInfo.TimingPointAt(position);
double beatSnapLength = timingPoint.BeatLength / beatDivisor;
// We will be snapping to beats within the timing point
position -= timingPoint.Time;
// Determine the index from the current timing point of the closest beat to position
int closestBeat = (int)Math.Round(position / beatSnapLength);
position = timingPoint.Time + closestBeat * beatSnapLength;
// Depending on beatSnapLength, we may snap to a beat that is beyond timingPoint's end time, but we want to instead snap to
// the next timing point's start time
var nextTimingPoint = ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time);
if (position > nextTimingPoint?.Time)
position = nextTimingPoint.Time;
return Seek(position);
}
/// <summary>
/// Seeks backwards by one beat length.
/// </summary>
/// <param name="snapped">Whether to snap to the closest beat after seeking.</param>
public void SeekBackward(bool snapped = false) => seek(-1, snapped);
/// <summary>
/// Seeks forwards by one beat length.
/// </summary>
/// <param name="snapped">Whether to snap to the closest beat after seeking.</param>
public void SeekForward(bool snapped = false) => seek(1, snapped);
private void seek(int direction, bool snapped)
{
var timingPoint = ControlPointInfo.TimingPointAt(CurrentTime);
if (direction < 0 && timingPoint.Time == CurrentTime)
{
// When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into
int activeIndex = ControlPointInfo.TimingPoints.IndexOf(timingPoint);
while (activeIndex > 0 && CurrentTime == timingPoint.Time)
timingPoint = ControlPointInfo.TimingPoints[--activeIndex];
}
double seekAmount = timingPoint.BeatLength / beatDivisor;
double seekTime = CurrentTime + seekAmount * direction;
if (!snapped || ControlPointInfo.TimingPoints.Count == 0)
{
Seek(seekTime);
return;
}
// We will be snapping to beats within timingPoint
seekTime -= timingPoint.Time;
// Determine the index from timingPoint of the closest beat to seekTime, accounting for scrolling direction
int closestBeat;
if (direction > 0)
closestBeat = (int)Math.Floor(seekTime / seekAmount);
else
closestBeat = (int)Math.Ceiling(seekTime / seekAmount);
seekTime = timingPoint.Time + closestBeat * seekAmount;
// Due to the rounding above, we may end up on the current beat. This will effectively cause 0 seeking to happen, but we don't want this.
// Instead, we'll go to the next beat in the direction when this is the case
if (Precision.AlmostEquals(CurrentTime, seekTime))
{
closestBeat += direction > 0 ? 1 : -1;
seekTime = timingPoint.Time + closestBeat * seekAmount;
}
if (seekTime < timingPoint.Time && timingPoint != ControlPointInfo.TimingPoints.First())
seekTime = timingPoint.Time;
var nextTimingPoint = ControlPointInfo.TimingPoints.FirstOrDefault(t => t.Time > timingPoint.Time);
if (seekTime > nextTimingPoint?.Time)
seekTime = nextTimingPoint.Time;
Seek(seekTime);
}
}
}

View File

@ -1,6 +1,7 @@
// 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 JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
@ -21,15 +22,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose
private Container composerContainer; private Container composerContainer;
private DependencyContainer dependencies; [BackgroundDependencyLoader(true)]
private void load([CanBeNull] BindableBeatDivisor beatDivisor)
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(parent);
[BackgroundDependencyLoader]
private void load()
{ {
dependencies.Cache(beatDivisor); if (beatDivisor != null)
this.beatDivisor.BindTo(beatDivisor);
ScrollableTimeline timeline; ScrollableTimeline timeline;
Children = new Drawable[] Children = new Drawable[]

View File

@ -222,7 +222,7 @@ namespace osu.Game.Screens.Menu
boxHoverLayer.FadeOut(800, Easing.OutExpo); boxHoverLayer.FadeOut(800, Easing.OutExpo);
} }
public override bool HandleKeyboardInput => state != ButtonState.Exploded; public override bool HandleKeyboardInput => state == ButtonState.Expanded;
public override bool HandleMouseInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; public override bool HandleMouseInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f;
protected override void Update() protected override void Update()

View File

@ -309,11 +309,6 @@ namespace osu.Game.Screens.Play
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
}, },
new MetadataLine("Composer", string.Empty)
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
},
new MetadataLine("Mapper", metadata.AuthorString) new MetadataLine("Mapper", metadata.AuthorString)
{ {
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,

View File

@ -66,7 +66,7 @@ namespace osu.Game.Screens.Select
get { return beatmapSets.Select(g => g.BeatmapSet); } get { return beatmapSets.Select(g => g.BeatmapSet); }
set set
{ {
CarouselGroup newRoot = new CarouselGroupEagerSelect(); CarouselRoot newRoot = new CarouselRoot(this);
Task.Run(() => Task.Run(() =>
{ {
@ -102,10 +102,11 @@ namespace osu.Game.Screens.Select
private readonly Stack<CarouselBeatmap> randomSelectedBeatmaps = new Stack<CarouselBeatmap>(); private readonly Stack<CarouselBeatmap> randomSelectedBeatmaps = new Stack<CarouselBeatmap>();
protected List<DrawableCarouselItem> Items = new List<DrawableCarouselItem>(); protected List<DrawableCarouselItem> Items = new List<DrawableCarouselItem>();
private CarouselGroup root = new CarouselGroupEagerSelect(); private CarouselRoot root;
public BeatmapCarousel() public BeatmapCarousel()
{ {
root = new CarouselRoot(this);
Child = new OsuContextMenuContainer Child = new OsuContextMenuContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
@ -627,5 +628,23 @@ namespace osu.Game.Screens.Select
// layer transformations on top, with a similar reasoning to the previous comment. // layer transformations on top, with a similar reasoning to the previous comment.
p.SetMultiplicativeAlpha(MathHelper.Clamp(1.75f - 1.5f * dist, 0, 1)); p.SetMultiplicativeAlpha(MathHelper.Clamp(1.75f - 1.5f * dist, 0, 1));
} }
private class CarouselRoot : CarouselGroupEagerSelect
{
private readonly BeatmapCarousel carousel;
public CarouselRoot(BeatmapCarousel carousel)
{
this.carousel = carousel;
}
protected override void PerformSelection()
{
if (LastSelected == null)
carousel.SelectNextRandom();
else
base.PerformSelection();
}
}
} }
} }

View File

@ -20,13 +20,16 @@ namespace osu.Game.Screens.Select.Carousel
}; };
} }
/// <summary>
/// The last selected item.
/// </summary>
protected CarouselItem LastSelected { get; private set; }
/// <summary> /// <summary>
/// We need to keep track of the index for cases where the selection is removed but we want to select a new item based on its old location. /// We need to keep track of the index for cases where the selection is removed but we want to select a new item based on its old location.
/// </summary> /// </summary>
private int lastSelectedIndex; private int lastSelectedIndex;
private CarouselItem lastSelected;
/// <summary> /// <summary>
/// To avoid overhead during filter operations, we don't attempt any selections until after all /// To avoid overhead during filter operations, we don't attempt any selections until after all
/// children have been filtered. This bool will be true during the base <see cref="Filter(FilterCriteria)"/> /// children have been filtered. This bool will be true during the base <see cref="Filter(FilterCriteria)"/>
@ -47,7 +50,7 @@ namespace osu.Game.Screens.Select.Carousel
{ {
base.RemoveChild(i); base.RemoveChild(i);
if (i != lastSelected) if (i != LastSelected)
updateSelectedIndex(); updateSelectedIndex();
} }
@ -83,6 +86,11 @@ namespace osu.Game.Screens.Select.Carousel
// we only perform eager selection if none of our children are in a selected state already. // we only perform eager selection if none of our children are in a selected state already.
if (Children.Any(i => i.State == CarouselItemState.Selected)) return; if (Children.Any(i => i.State == CarouselItemState.Selected)) return;
PerformSelection();
}
protected virtual void PerformSelection()
{
CarouselItem nextToSelect = CarouselItem nextToSelect =
Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered) ?? Children.Skip(lastSelectedIndex).FirstOrDefault(i => !i.Filtered) ??
Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered); Children.Reverse().Skip(InternalChildren.Count - lastSelectedIndex).FirstOrDefault(i => !i.Filtered);
@ -95,10 +103,10 @@ namespace osu.Game.Screens.Select.Carousel
private void updateSelected(CarouselItem newSelection) private void updateSelected(CarouselItem newSelection)
{ {
lastSelected = newSelection; LastSelected = newSelection;
updateSelectedIndex(); updateSelectedIndex();
} }
private void updateSelectedIndex() => lastSelectedIndex = lastSelected == null ? 0 : Math.Max(0, InternalChildren.IndexOf(lastSelected)); private void updateSelectedIndex() => lastSelectedIndex = LastSelected == null ? 0 : Math.Max(0, InternalChildren.IndexOf(LastSelected));
} }
} }

View File

@ -39,8 +39,9 @@ namespace osu.Game.Screens.Select.Leaderboards
private readonly LoadingAnimation loading; private readonly LoadingAnimation loading;
private IEnumerable<Score> scores; private ScheduledDelegate showScoresDelegate;
private IEnumerable<Score> scores;
public IEnumerable<Score> Scores public IEnumerable<Score> Scores
{ {
get { return scores; } get { return scores; }
@ -59,22 +60,28 @@ namespace osu.Game.Screens.Select.Leaderboards
// ensure placeholder is hidden when displaying scores // ensure placeholder is hidden when displaying scores
PlaceholderState = PlaceholderState.Successful; PlaceholderState = PlaceholderState.Successful;
// schedule because we may not be loaded yet (LoadComponentAsync complains). var flow = scrollFlow = new FillFlowContainer<LeaderboardScore>
Schedule(() =>
{
LoadComponentAsync(new FillFlowContainer<LeaderboardScore>
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 5f), Spacing = new Vector2(0f, 5f),
Padding = new MarginPadding { Top = 10, Bottom = 5 }, Padding = new MarginPadding { Top = 10, Bottom = 5 },
ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) }) ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
}, f => };
// schedule because we may not be loaded yet (LoadComponentAsync complains).
showScoresDelegate?.Cancel();
if (!IsLoaded)
showScoresDelegate = Schedule(showScores);
else
showScores();
void showScores() => LoadComponentAsync(flow, _ =>
{ {
scrollContainer.Add(scrollFlow = f); scrollContainer.Add(flow);
int i = 0; int i = 0;
foreach (var s in f.Children) foreach (var s in flow.Children)
{ {
using (s.BeginDelayedSequence(i++ * 50, true)) using (s.BeginDelayedSequence(i++ * 50, true))
s.Show(); s.Show();
@ -82,7 +89,6 @@ namespace osu.Game.Screens.Select.Leaderboards
scrollContainer.ScrollTo(0f, false); scrollContainer.ScrollTo(0f, false);
}); });
});
} }
} }
@ -103,6 +109,10 @@ namespace osu.Game.Screens.Select.Leaderboards
private PlaceholderState placeholderState; private PlaceholderState placeholderState;
/// <summary>
/// Update the placeholder visibility.
/// Setting this to anything other than PlaceholderState.Successful will cancel all existing retrieval requests and hide scores.
/// </summary>
protected PlaceholderState PlaceholderState protected PlaceholderState PlaceholderState
{ {
get { return placeholderState; } get { return placeholderState; }
@ -250,43 +260,45 @@ namespace osu.Game.Screens.Select.Leaderboards
loading.Show(); loading.Show();
getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope); getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope);
getScoresRequest.Success += r => getScoresRequest.Success += r => Schedule(() =>
{ {
Scores = r.Scores; Scores = r.Scores;
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
}; });
getScoresRequest.Failure += onUpdateFailed;
api.Queue(getScoresRequest); getScoresRequest.Failure += e => Schedule(() =>
}
private void onUpdateFailed(Exception e)
{ {
if (e is OperationCanceledException) if (e is OperationCanceledException)
return; return;
PlaceholderState = PlaceholderState.NetworkFailure; PlaceholderState = PlaceholderState.NetworkFailure;
Logger.Error(e, @"Couldn't fetch beatmap scores!"); Logger.Error(e, @"Couldn't fetch beatmap scores!");
});
api.Queue(getScoresRequest);
} }
private Placeholder currentPlaceholder;
private void replacePlaceholder(Placeholder placeholder) private void replacePlaceholder(Placeholder placeholder)
{ {
var existingPlaceholder = placeholderContainer.Children.LastOrDefault() as Placeholder; if (placeholder != null && placeholder.Equals(currentPlaceholder))
if (placeholder != null && placeholder.Equals(existingPlaceholder))
return; return;
existingPlaceholder?.FadeOut(150, Easing.OutQuint).Expire(); currentPlaceholder?.FadeOut(150, Easing.OutQuint).Expire();
if (placeholder == null) if (placeholder == null)
{
currentPlaceholder = null;
return; return;
}
Scores = null; placeholderContainer.Child = placeholder;
placeholderContainer.Add(placeholder);
placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint); placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint);
placeholder.FadeInFromZero(fade_duration, Easing.OutQuint); placeholder.FadeInFromZero(fade_duration, Easing.OutQuint);
currentPlaceholder = placeholder;
} }
protected override void Update() protected override void Update()

View File

@ -0,0 +1,79 @@
// 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.Allocation;
using osu.Framework.Input;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Screens.Compose;
namespace osu.Game.Tests.Visual
{
/// <summary>
/// Provides a clock, beat-divisor, and scrolling capability for test cases of editor components that
/// are preferrably tested within the presence of a clock and seek controls.
/// </summary>
public abstract class EditorClockTestCase : OsuTestCase
{
protected readonly BindableBeatDivisor BeatDivisor = new BindableBeatDivisor();
protected readonly EditorClock Clock;
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(parent);
private OsuGameBase osuGame;
protected EditorClockTestCase()
{
Clock = new EditorClock(new ControlPointInfo(), BeatDivisor) { IsCoupled = false };
}
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame)
{
this.osuGame = osuGame;
dependencies.Cache(BeatDivisor);
dependencies.CacheAs<IFrameBasedClock>(Clock);
dependencies.CacheAs<IAdjustableClock>(Clock);
osuGame.Beatmap.ValueChanged += beatmapChanged;
beatmapChanged(osuGame.Beatmap.Value);
}
private void beatmapChanged(WorkingBeatmap working)
{
Clock.ControlPointInfo = working.Beatmap.ControlPointInfo;
Clock.ChangeSource((IAdjustableClock)working.Track ?? new StopwatchClock());
Clock.ProcessFrame();
}
protected override void Update()
{
base.Update();
Clock.ProcessFrame();
}
protected override bool OnWheel(InputState state)
{
if (state.Mouse.WheelDelta > 0)
Clock.SeekBackward(true);
else
Clock.SeekForward(true);
return true;
}
protected override void Dispose(bool isDisposing)
{
osuGame.Beatmap.ValueChanged -= beatmapChanged;
base.Dispose(isDisposing);
}
}
}

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 System;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using osu.Framework.Testing; using osu.Framework.Testing;
@ -10,28 +9,27 @@ namespace osu.Game.Tests.Visual
{ {
public abstract class OsuTestCase : TestCase public abstract class OsuTestCase : TestCase
{ {
public override void RunTest() protected override ITestCaseTestRunner CreateRunner() => new OsuTestCaseTestRunner();
{
using (var host = new CleanRunHeadlessGameHost($"test-{Guid.NewGuid()}", realtime: false))
host.Run(new OsuTestCaseTestRunner(this));
}
public class OsuTestCaseTestRunner : OsuGameBase public class OsuTestCaseTestRunner : OsuGameBase, ITestCaseTestRunner
{ {
private readonly OsuTestCase testCase;
protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location; protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location;
public OsuTestCaseTestRunner(OsuTestCase testCase) private readonly TestCaseTestRunner.TestRunner runner;
public OsuTestCaseTestRunner()
{ {
this.testCase = testCase; runner = new TestCaseTestRunner.TestRunner();
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
Add(new TestCaseTestRunner.TestRunner(testCase));
} Add(runner);
}
public void RunTestBlocking(TestCase test) => runner.RunTestBlocking(test);
} }
} }
} }

20
osu.Game/Users/Badge.cs Normal file
View File

@ -0,0 +1,20 @@
// 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 Newtonsoft.Json;
namespace osu.Game.Users
{
public class Badge
{
[JsonProperty("awarded_at")]
public DateTimeOffset AwardedAt;
[JsonProperty("description")]
public string Description;
[JsonProperty("image_url")]
public string ImageUrl;
}
}

View File

@ -137,6 +137,9 @@ namespace osu.Game.Users
[JsonProperty(@"rankHistory")] [JsonProperty(@"rankHistory")]
public RankHistoryData RankHistory; public RankHistoryData RankHistory;
[JsonProperty("badges")]
public Badge[] Badges;
public override string ToString() => Username; public override string ToString() => Username;
} }
} }

View File

@ -17,7 +17,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile.Header;
namespace osu.Game.Users namespace osu.Game.Users
{ {

View File

@ -20,7 +20,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="SharpCompress" Version="0.18.1" /> <PackageReference Include="SharpCompress" Version="0.18.1" />
<PackageReference Include="NUnit" Version="3.8.1" /> <PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="4.4.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -15,7 +15,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.1" />
<PackageReference Include="DeepEqual" Version="1.6.0" /> <PackageReference Include="DeepEqual" Version="1.6.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.1" />
<PackageReference Include="NUnit" Version="3.8.1" /> <PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>