mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 04:02:57 +08:00
Merge branch 'master' into bubble_mod_implementation_clean
This commit is contained in:
commit
5024838f3a
@ -17,7 +17,7 @@
|
|||||||
<EmbeddedResource Include="Resources\**\*.*" />
|
<EmbeddedResource Include="Resources\**\*.*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Code Analysis">
|
<ItemGroup Label="Code Analysis">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" PrivateAssets="All" />
|
||||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Code Analysis">
|
<PropertyGroup Label="Code Analysis">
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.EmptyFreeform\osu.Game.Rulesets.EmptyFreeform.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.EmptyFreeform\osu.Game.Rulesets.EmptyFreeform.csproj" />
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.EmptyScrolling\osu.Game.Rulesets.EmptyScrolling.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.EmptyScrolling\osu.Game.Rulesets.EmptyScrolling.csproj" />
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
||||||
|
@ -98,7 +98,7 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
if (status.Value is UserStatusOnline && activity.Value != null)
|
if (status.Value is UserStatusOnline && activity.Value != null)
|
||||||
{
|
{
|
||||||
presence.State = truncate(activity.Value.Status);
|
presence.State = truncate(activity.Value.GetStatus(privacyMode.Value == DiscordRichPresenceMode.Limited));
|
||||||
presence.Details = truncate(getDetails(activity.Value));
|
presence.Details = truncate(getDetails(activity.Value));
|
||||||
|
|
||||||
if (getBeatmap(activity.Value) is IBeatmapInfo beatmap && beatmap.OnlineID > 0)
|
if (getBeatmap(activity.Value) is IBeatmapInfo beatmap && beatmap.OnlineID > 0)
|
||||||
@ -169,7 +169,7 @@ namespace osu.Desktop
|
|||||||
case UserActivity.InGame game:
|
case UserActivity.InGame game:
|
||||||
return game.BeatmapInfo;
|
return game.BeatmapInfo;
|
||||||
|
|
||||||
case UserActivity.Editing edit:
|
case UserActivity.EditingBeatmap edit:
|
||||||
return edit.BeatmapInfo;
|
return edit.BeatmapInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,9 +183,12 @@ namespace osu.Desktop
|
|||||||
case UserActivity.InGame game:
|
case UserActivity.InGame game:
|
||||||
return game.BeatmapInfo.ToString() ?? string.Empty;
|
return game.BeatmapInfo.ToString() ?? string.Empty;
|
||||||
|
|
||||||
case UserActivity.Editing edit:
|
case UserActivity.EditingBeatmap edit:
|
||||||
return edit.BeatmapInfo.ToString() ?? string.Empty;
|
return edit.BeatmapInfo.ToString() ?? string.Empty;
|
||||||
|
|
||||||
|
case UserActivity.WatchingReplay watching:
|
||||||
|
return watching.BeatmapInfo.ToString();
|
||||||
|
|
||||||
case UserActivity.InLobby lobby:
|
case UserActivity.InLobby lobby:
|
||||||
return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;
|
return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
|
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
|
||||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||||
<PackageReference Include="System.IO.Packaging" Version="6.0.0" />
|
<PackageReference Include="System.IO.Packaging" Version="7.0.0" />
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.1.1.14" />
|
<PackageReference Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Resources">
|
<ItemGroup Label="Resources">
|
||||||
<EmbeddedResource Include="lazer.ico" />
|
<EmbeddedResource Include="lazer.ico" />
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.4" />
|
||||||
<PackageReference Include="nunit" Version="3.13.3" />
|
<PackageReference Include="nunit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class ManiaScrollSlider : OsuSliderBar<double>
|
private partial class ManiaScrollSlider : RoundedSliderBar<double>
|
||||||
{
|
{
|
||||||
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(Current.Value, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / Current.Value));
|
public override LocalisableString TooltipText => RulesetSettingsStrings.ScrollSpeedTooltip(Current.Value, (int)Math.Round(DrawableManiaRuleset.MAX_TIME_RANGE / Current.Value));
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,15 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Animations;
|
using osu.Framework.Graphics.Animations;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
@ -34,6 +37,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
private Drawable? lightContainer;
|
private Drawable? lightContainer;
|
||||||
|
|
||||||
private Drawable? light;
|
private Drawable? light;
|
||||||
|
private LegacyNoteBodyStyle? bodyStyle;
|
||||||
|
|
||||||
public LegacyBodyPiece()
|
public LegacyBodyPiece()
|
||||||
{
|
{
|
||||||
@ -54,9 +58,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
float lightScale = GetColumnSkinConfig<float>(skin, LegacyManiaSkinConfigurationLookups.HoldNoteLightScale)?.Value
|
float lightScale = GetColumnSkinConfig<float>(skin, LegacyManiaSkinConfigurationLookups.HoldNoteLightScale)?.Value
|
||||||
?? 1;
|
?? 1;
|
||||||
|
|
||||||
float minimumColumnWidth = GetColumnSkinConfig<float>(skin, LegacyManiaSkinConfigurationLookups.MinimumColumnWidth)?.Value
|
|
||||||
?? 1;
|
|
||||||
|
|
||||||
// Create a temporary animation to retrieve the number of frames, in an effort to calculate the intended frame length.
|
// Create a temporary animation to retrieve the number of frames, in an effort to calculate the intended frame length.
|
||||||
// This animation is discarded and re-queried with the appropriate frame length afterwards.
|
// This animation is discarded and re-queried with the appropriate frame length afterwards.
|
||||||
var tmp = skin.GetAnimation(lightImage, true, false);
|
var tmp = skin.GetAnimation(lightImage, true, false);
|
||||||
@ -83,7 +84,14 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
bodySprite = skin.GetAnimation(imageName, WrapMode.ClampToEdge, WrapMode.ClampToEdge, true, true).With(d =>
|
bodyStyle = skin.GetConfig<ManiaSkinConfigurationLookup, LegacyNoteBodyStyle>(new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.NoteBodyStyle))?.Value;
|
||||||
|
|
||||||
|
var wrapMode = bodyStyle == LegacyNoteBodyStyle.Stretch ? WrapMode.ClampToEdge : WrapMode.Repeat;
|
||||||
|
|
||||||
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
|
isHitting.BindTo(holdNote.IsHitting);
|
||||||
|
|
||||||
|
bodySprite = skin.GetAnimation(imageName, wrapMode, wrapMode, true, true).With(d =>
|
||||||
{
|
{
|
||||||
if (d == null)
|
if (d == null)
|
||||||
return;
|
return;
|
||||||
@ -94,16 +102,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
d.Anchor = Anchor.TopCentre;
|
d.Anchor = Anchor.TopCentre;
|
||||||
d.RelativeSizeAxes = Axes.Both;
|
d.RelativeSizeAxes = Axes.Both;
|
||||||
d.Size = Vector2.One;
|
d.Size = Vector2.One;
|
||||||
d.FillMode = FillMode.Stretch;
|
|
||||||
d.Height = minimumColumnWidth / d.DrawWidth * 1.6f; // constant matching stable.
|
|
||||||
// Todo: Wrap?
|
// Todo: Wrap?
|
||||||
});
|
});
|
||||||
|
|
||||||
if (bodySprite != null)
|
if (bodySprite != null)
|
||||||
InternalChild = bodySprite;
|
InternalChild = bodySprite;
|
||||||
|
|
||||||
direction.BindTo(scrollingInfo.Direction);
|
|
||||||
isHitting.BindTo(holdNote.IsHitting);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -165,7 +168,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
if (bodySprite != null)
|
if (bodySprite != null)
|
||||||
{
|
{
|
||||||
bodySprite.Origin = Anchor.BottomCentre;
|
bodySprite.Origin = Anchor.BottomCentre;
|
||||||
bodySprite.Scale = new Vector2(1, -1);
|
bodySprite.Scale = new Vector2(bodySprite.Scale.X, Math.Abs(bodySprite.Scale.Y) * -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (light != null)
|
if (light != null)
|
||||||
@ -176,7 +179,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
if (bodySprite != null)
|
if (bodySprite != null)
|
||||||
{
|
{
|
||||||
bodySprite.Origin = Anchor.TopCentre;
|
bodySprite.Origin = Anchor.TopCentre;
|
||||||
bodySprite.Scale = Vector2.One;
|
bodySprite.Scale = new Vector2(bodySprite.Scale.X, Math.Abs(bodySprite.Scale.Y));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (light != null)
|
if (light != null)
|
||||||
@ -207,6 +210,29 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
missFadeTime.Value ??= holdNote.HoldBrokenTime;
|
missFadeTime.Value ??= holdNote.HoldBrokenTime;
|
||||||
|
|
||||||
|
// here we go...
|
||||||
|
switch (bodyStyle)
|
||||||
|
{
|
||||||
|
case LegacyNoteBodyStyle.Stretch:
|
||||||
|
// this is how lazer works by default. nothing required.
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// this is where things get fucked up.
|
||||||
|
// honestly there's three modes to handle here but they seem really pointless?
|
||||||
|
// let's wait to see if anyone actually uses them in skins.
|
||||||
|
if (bodySprite != null)
|
||||||
|
{
|
||||||
|
var sprite = bodySprite as Sprite ?? bodySprite.ChildrenOfType<Sprite>().Single();
|
||||||
|
|
||||||
|
bodySprite.FillMode = FillMode.Stretch;
|
||||||
|
// i dunno this looks about right??
|
||||||
|
bodySprite.Scale = new Vector2(1, 32800 / sprite.DrawHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="Moq" Version="4.18.2" />
|
<PackageReference Include="Moq" Version="4.18.4" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
[TestCase("slider-conversion-v6")]
|
[TestCase("slider-conversion-v6")]
|
||||||
[TestCase("slider-conversion-v14")]
|
[TestCase("slider-conversion-v14")]
|
||||||
[TestCase("slider-generating-drumroll-2")]
|
[TestCase("slider-generating-drumroll-2")]
|
||||||
|
[TestCase("file-hitsamples")]
|
||||||
public void Test(string name) => base.Test(name);
|
public void Test(string name) => base.Test(name);
|
||||||
|
|
||||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -2,13 +2,15 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.UI;
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Mods
|
namespace osu.Game.Rulesets.Taiko.Mods
|
||||||
{
|
{
|
||||||
public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset<TaikoHitObject>
|
public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset<TaikoHitObject>, IApplicableToDrawableHitObject
|
||||||
{
|
{
|
||||||
public void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
@ -18,5 +20,11 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
var playfield = (TaikoPlayfield)drawableRuleset.Playfield;
|
var playfield = (TaikoPlayfield)drawableRuleset.Playfield;
|
||||||
playfield.ClassicHitTargetPosition.Value = true;
|
playfield.ClassicHitTargetPosition.Value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ApplyToDrawableHitObject(DrawableHitObject drawable)
|
||||||
|
{
|
||||||
|
if (drawable is DrawableTaikoHitObject hit)
|
||||||
|
hit.SnapJudgementLocation = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,6 +207,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
const float gravity_time = 300;
|
const float gravity_time = 300;
|
||||||
const float gravity_travel_height = 200;
|
const float gravity_travel_height = 200;
|
||||||
|
|
||||||
|
if (SnapJudgementLocation)
|
||||||
|
MainPiece.MoveToX(-X);
|
||||||
|
|
||||||
this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad);
|
this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad);
|
||||||
|
|
||||||
this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out)
|
this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out)
|
||||||
|
@ -25,6 +25,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
|
|
||||||
private readonly Container nonProxiedContent;
|
private readonly Container nonProxiedContent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the location of the hit should be snapped to the hit target before animating.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is how osu-stable worked, but notably is not how TnT works.
|
||||||
|
/// Not snapping results in less visual feedback on hit accuracy.
|
||||||
|
/// </remarks>
|
||||||
|
public bool SnapJudgementLocation { get; set; }
|
||||||
|
|
||||||
protected DrawableTaikoHitObject([CanBeNull] TaikoHitObject hitObject)
|
protected DrawableTaikoHitObject([CanBeNull] TaikoHitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
{"Mappings":[{"StartTime":500.0,"Objects":[{"StartTime":500.0,"EndTime":500.0,"IsRim":false,"IsCentre":true,"IsDrumRoll":false,"IsSwell":false,"IsStrong":false}]},{"StartTime":1000.0,"Objects":[{"StartTime":1000.0,"EndTime":1000.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":false}]},{"StartTime":1500.0,"Objects":[{"StartTime":1500.0,"EndTime":1500.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":false}]},{"StartTime":2000.0,"Objects":[{"StartTime":2000.0,"EndTime":2000.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":false}]},{"StartTime":2500.0,"Objects":[{"StartTime":2500.0,"EndTime":2500.0,"IsRim":false,"IsCentre":true,"IsDrumRoll":false,"IsSwell":false,"IsStrong":true}]},{"StartTime":3000.0,"Objects":[{"StartTime":3000.0,"EndTime":3000.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":true}]},{"StartTime":3500.0,"Objects":[{"StartTime":3500.0,"EndTime":3500.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":true}]},{"StartTime":4000.0,"Objects":[{"StartTime":4000.0,"EndTime":4000.0,"IsRim":true,"IsCentre":false,"IsDrumRoll":false,"IsSwell":false,"IsStrong":true}]}]}
|
@ -0,0 +1,22 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[Difficulty]
|
||||||
|
HPDrainRate:5
|
||||||
|
CircleSize:7
|
||||||
|
OverallDifficulty:6.5
|
||||||
|
ApproachRate:10
|
||||||
|
SliderMultiplier:1.9
|
||||||
|
SliderTickRate:1
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
500,500,4,2,1,50,1,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
256,192,500,1,0,0:0:0:0:sample.ogg
|
||||||
|
256,192,1000,1,8,0:0:0:0:sample.ogg
|
||||||
|
256,192,1500,1,2,0:0:0:0:sample.ogg
|
||||||
|
256,192,2000,1,10,0:0:0:0:sample.ogg
|
||||||
|
256,192,2500,1,4,0:0:0:0:sample.ogg
|
||||||
|
256,192,3000,1,12,0:0:0:0:sample.ogg
|
||||||
|
256,192,3500,1,6,0:0:0:0:sample.ogg
|
||||||
|
256,192,4000,1,14,0:0:0:0:sample.ogg
|
@ -209,9 +209,8 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
HitResult.Great,
|
HitResult.Great,
|
||||||
HitResult.Ok,
|
HitResult.Ok,
|
||||||
|
|
||||||
HitResult.SmallTickHit,
|
|
||||||
|
|
||||||
HitResult.SmallBonus,
|
HitResult.SmallBonus,
|
||||||
|
HitResult.LargeBonus,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,6 +219,9 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
case HitResult.SmallBonus:
|
case HitResult.SmallBonus:
|
||||||
|
return "drum tick";
|
||||||
|
|
||||||
|
case HitResult.LargeBonus:
|
||||||
return "bonus";
|
return "bonus";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +214,9 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.That(oneTime.EndTime, Is.EqualTo(4000 + loop_duration));
|
Assert.That(oneTime.EndTime, Is.EqualTo(4000 + loop_duration));
|
||||||
|
|
||||||
StoryboardSprite manyTimes = background.Elements.OfType<StoryboardSprite>().Single(s => s.Path == "many-times.png");
|
StoryboardSprite manyTimes = background.Elements.OfType<StoryboardSprite>().Single(s => s.Path == "many-times.png");
|
||||||
Assert.That(manyTimes.EndTime, Is.EqualTo(9000 + 40 * loop_duration));
|
// It is intentional that we don't consider the loop count (40) as part of the end time calculation to match stable's handling.
|
||||||
|
// If we were to include the loop count, storyboards which loop for stupid long loop counts would continue playing the outro forever.
|
||||||
|
Assert.That(manyTimes.EndTime, Is.EqualTo(9000 + loop_duration));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
@ -12,7 +10,7 @@ using osu.Game.Screens.Edit;
|
|||||||
namespace osu.Game.Tests.Editing
|
namespace osu.Game.Tests.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class EditorChangeHandlerTest
|
public class BeatmapEditorChangeHandlerTest
|
||||||
{
|
{
|
||||||
private int stateChangedFired;
|
private int stateChangedFired;
|
||||||
|
|
||||||
@ -23,18 +21,23 @@ namespace osu.Game.Tests.Editing
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSaveRestoreState()
|
public void TestSaveRestoreStateUsingTransaction()
|
||||||
{
|
{
|
||||||
var (handler, beatmap) = createChangeHandler();
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
addArbitraryChange(beatmap);
|
handler.BeginChange();
|
||||||
handler.SaveState();
|
|
||||||
|
|
||||||
|
// Initial state will be saved on BeginChange
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(1));
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
|
handler.EndChange();
|
||||||
|
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
@ -43,7 +46,35 @@ namespace osu.Game.Tests.Editing
|
|||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.True);
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
|
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSaveRestoreState()
|
||||||
|
{
|
||||||
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
// Save initial state
|
||||||
|
handler.SaveState();
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
|
handler.SaveState();
|
||||||
|
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(2));
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
handler.RestoreState(-1);
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
|
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -54,6 +85,10 @@ namespace osu.Game.Tests.Editing
|
|||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
// Save initial state
|
||||||
|
handler.SaveState();
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
string originalHash = handler.CurrentStateHash;
|
string originalHash = handler.CurrentStateHash;
|
||||||
|
|
||||||
addArbitraryChange(beatmap);
|
addArbitraryChange(beatmap);
|
||||||
@ -61,7 +96,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(1));
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
|
|
||||||
string hash = handler.CurrentStateHash;
|
string hash = handler.CurrentStateHash;
|
||||||
|
|
||||||
@ -69,7 +104,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
handler.RestoreState(-1);
|
handler.RestoreState(-1);
|
||||||
|
|
||||||
Assert.That(originalHash, Is.EqualTo(handler.CurrentStateHash));
|
Assert.That(originalHash, Is.EqualTo(handler.CurrentStateHash));
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(2));
|
Assert.That(stateChangedFired, Is.EqualTo(3));
|
||||||
|
|
||||||
addArbitraryChange(beatmap);
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
@ -84,12 +119,16 @@ namespace osu.Game.Tests.Editing
|
|||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
// Save initial state
|
||||||
|
handler.SaveState();
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
addArbitraryChange(beatmap);
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(1));
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
|
|
||||||
string hash = handler.CurrentStateHash;
|
string hash = handler.CurrentStateHash;
|
||||||
|
|
||||||
@ -97,7 +136,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
|
||||||
Assert.That(hash, Is.EqualTo(handler.CurrentStateHash));
|
Assert.That(hash, Is.EqualTo(handler.CurrentStateHash));
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(1));
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
|
|
||||||
handler.RestoreState(-1);
|
handler.RestoreState(-1);
|
||||||
|
|
||||||
@ -106,7 +145,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
// we should only be able to restore once even though we saved twice.
|
// we should only be able to restore once even though we saved twice.
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.True);
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(2));
|
Assert.That(stateChangedFired, Is.EqualTo(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -114,11 +153,15 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var (handler, beatmap) = createChangeHandler();
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
|
// Save initial state
|
||||||
|
handler.SaveState();
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
|
||||||
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++)
|
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++)
|
||||||
{
|
{
|
||||||
Assert.That(stateChangedFired, Is.EqualTo(i));
|
Assert.That(stateChangedFired, Is.EqualTo(i + 1));
|
||||||
|
|
||||||
addArbitraryChange(beatmap);
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
@ -169,7 +212,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var changeHandler = new EditorChangeHandler(beatmap);
|
var changeHandler = new BeatmapEditorChangeHandler(beatmap);
|
||||||
|
|
||||||
changeHandler.OnStateChange += () => stateChangedFired++;
|
changeHandler.OnStateChange += () => stateChangedFired++;
|
||||||
return (changeHandler, beatmap);
|
return (changeHandler, beatmap);
|
@ -31,8 +31,8 @@ namespace osu.Game.Tests.Visual.Audio
|
|||||||
|
|
||||||
private WaveformTestBeatmap beatmap;
|
private WaveformTestBeatmap beatmap;
|
||||||
|
|
||||||
private OsuSliderBar<int> lowPassSlider;
|
private RoundedSliderBar<int> lowPassSlider;
|
||||||
private OsuSliderBar<int> highPassSlider;
|
private RoundedSliderBar<int> highPassSlider;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Audio
|
|||||||
Text = $"Low Pass: {lowPassFilter.Cutoff}hz",
|
Text = $"Low Pass: {lowPassFilter.Cutoff}hz",
|
||||||
Font = new FontUsage(size: 40)
|
Font = new FontUsage(size: 40)
|
||||||
},
|
},
|
||||||
lowPassSlider = new OsuSliderBar<int>
|
lowPassSlider = new RoundedSliderBar<int>
|
||||||
{
|
{
|
||||||
Width = 500,
|
Width = 500,
|
||||||
Height = 50,
|
Height = 50,
|
||||||
@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Audio
|
|||||||
Text = $"High Pass: {highPassFilter.Cutoff}hz",
|
Text = $"High Pass: {highPassFilter.Cutoff}hz",
|
||||||
Font = new FontUsage(size: 40)
|
Font = new FontUsage(size: 40)
|
||||||
},
|
},
|
||||||
highPassSlider = new OsuSliderBar<int>
|
highPassSlider = new RoundedSliderBar<int>
|
||||||
{
|
{
|
||||||
Width = 500,
|
Width = 500,
|
||||||
Height = 50,
|
Height = 50,
|
||||||
|
24
osu.Game.Tests/Visual/Gameplay/TestSceneLetterboxOverlay.cs
Normal file
24
osu.Game.Tests/Visual/Gameplay/TestSceneLetterboxOverlay.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Screens.Play.Break;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public partial class TestSceneLetterboxOverlay : OsuTestScene
|
||||||
|
{
|
||||||
|
public TestSceneLetterboxOverlay()
|
||||||
|
{
|
||||||
|
AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
new LetterboxOverlay()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,64 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
using System.Linq;
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
public partial class TestSceneReplayPlayer : RateAdjustedBeatmapTestScene
|
public partial class TestSceneReplayPlayer : RateAdjustedBeatmapTestScene
|
||||||
{
|
{
|
||||||
protected TestReplayPlayer Player;
|
protected TestReplayPlayer Player = null!;
|
||||||
|
|
||||||
public override void SetUpSteps()
|
|
||||||
{
|
|
||||||
base.SetUpSteps();
|
|
||||||
|
|
||||||
AddStep("Initialise player", () => Player = CreatePlayer(new OsuRuleset()));
|
|
||||||
AddStep("Load player", () => LoadScreen(Player));
|
|
||||||
AddUntilStep("player loaded", () => Player.IsLoaded);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestPauseViaSpace()
|
public void TestPauseViaSpace()
|
||||||
{
|
{
|
||||||
|
loadPlayerWithBeatmap();
|
||||||
|
|
||||||
|
double? lastTime = null;
|
||||||
|
|
||||||
|
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||||
|
|
||||||
|
AddStep("Pause playback with space", () => InputManager.Key(Key.Space));
|
||||||
|
|
||||||
|
AddAssert("player not exited", () => Player.IsCurrentScreen());
|
||||||
|
|
||||||
|
AddUntilStep("Time stopped progressing", () =>
|
||||||
|
{
|
||||||
|
double current = Player.GameplayClockContainer.CurrentTime;
|
||||||
|
bool changed = lastTime != current;
|
||||||
|
lastTime = current;
|
||||||
|
|
||||||
|
return !changed;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddWaitStep("wait some", 10);
|
||||||
|
|
||||||
|
AddAssert("Time still stopped", () => lastTime == Player.GameplayClockContainer.CurrentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPauseViaSpaceWithSkip()
|
||||||
|
{
|
||||||
|
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||||
|
{
|
||||||
|
BeatmapInfo = { AudioLeadIn = 60000 }
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for skip overlay", () => Player.ChildrenOfType<SkipOverlay>().First().IsButtonVisible);
|
||||||
|
|
||||||
|
AddStep("Skip with space", () => InputManager.Key(Key.Space));
|
||||||
|
|
||||||
|
AddAssert("Player not paused", () => !Player.DrawableRuleset.IsPaused.Value);
|
||||||
|
|
||||||
double? lastTime = null;
|
double? lastTime = null;
|
||||||
|
|
||||||
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||||
@ -52,6 +84,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestPauseViaMiddleMouse()
|
public void TestPauseViaMiddleMouse()
|
||||||
{
|
{
|
||||||
|
loadPlayerWithBeatmap();
|
||||||
|
|
||||||
double? lastTime = null;
|
double? lastTime = null;
|
||||||
|
|
||||||
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||||
@ -77,6 +111,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSeekBackwards()
|
public void TestSeekBackwards()
|
||||||
{
|
{
|
||||||
|
loadPlayerWithBeatmap();
|
||||||
|
|
||||||
double? lastTime = null;
|
double? lastTime = null;
|
||||||
|
|
||||||
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||||
@ -93,6 +129,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSeekForwards()
|
public void TestSeekForwards()
|
||||||
{
|
{
|
||||||
|
loadPlayerWithBeatmap();
|
||||||
|
|
||||||
double? lastTime = null;
|
double? lastTime = null;
|
||||||
|
|
||||||
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
AddUntilStep("wait for first hit", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||||
@ -106,12 +144,26 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddAssert("Jumped forwards", () => Player.GameplayClockContainer.CurrentTime - lastTime > 500);
|
AddAssert("Jumped forwards", () => Player.GameplayClockContainer.CurrentTime - lastTime > 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TestReplayPlayer CreatePlayer(Ruleset ruleset)
|
private void loadPlayerWithBeatmap(IBeatmap? beatmap = null)
|
||||||
{
|
{
|
||||||
Beatmap.Value = CreateWorkingBeatmap(ruleset.RulesetInfo);
|
AddStep("create player", () =>
|
||||||
|
{
|
||||||
|
CreatePlayer(new OsuRuleset(), beatmap);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("Load player", () => LoadScreen(Player));
|
||||||
|
AddUntilStep("player loaded", () => Player.IsLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void CreatePlayer(Ruleset ruleset, IBeatmap? beatmap = null)
|
||||||
|
{
|
||||||
|
Beatmap.Value = beatmap != null
|
||||||
|
? CreateWorkingBeatmap(beatmap)
|
||||||
|
: CreateWorkingBeatmap(ruleset.RulesetInfo);
|
||||||
|
|
||||||
SelectedMods.Value = new[] { ruleset.GetAutoplayMod() };
|
SelectedMods.Value = new[] { ruleset.GetAutoplayMod() };
|
||||||
|
|
||||||
return new TestReplayPlayer(false);
|
Player = new TestReplayPlayer(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
92
osu.Game.Tests/Visual/Online/TestSceneGroupBadges.cs
Normal file
92
osu.Game.Tests/Visual/Online/TestSceneGroupBadges.cs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Overlays.Profile.Header.Components;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public partial class TestSceneGroupBadges : OsuTestScene
|
||||||
|
{
|
||||||
|
public TestSceneGroupBadges()
|
||||||
|
{
|
||||||
|
var groups = new[]
|
||||||
|
{
|
||||||
|
new APIUser(),
|
||||||
|
new APIUser
|
||||||
|
{
|
||||||
|
Groups = new[]
|
||||||
|
{
|
||||||
|
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new APIUser
|
||||||
|
{
|
||||||
|
Groups = new[]
|
||||||
|
{
|
||||||
|
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
||||||
|
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new APIUser
|
||||||
|
{
|
||||||
|
Groups = new[]
|
||||||
|
{
|
||||||
|
new APIUserGroup { Colour = "#0066FF", ShortName = "PPY", Name = "peppy" },
|
||||||
|
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
||||||
|
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new APIUser
|
||||||
|
{
|
||||||
|
Groups = new[]
|
||||||
|
{
|
||||||
|
new APIUserGroup { Colour = "#0066FF", ShortName = "PPY", Name = "peppy" },
|
||||||
|
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
||||||
|
new APIUserGroup { Colour = "#999999", ShortName = "ALM", Name = "osu! Alumni" },
|
||||||
|
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } },
|
||||||
|
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators (Probationary)", Playmodes = new[] { "osu", "taiko" }, IsProbationary = true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Colour4.DarkGray
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(40),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new FillFlowContainer<GroupBadgeFlow>
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
ChildrenEnumerable = groups.Select(g => new GroupBadgeFlow { User = { Value = g } })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestEditActivity()
|
public void TestEditActivity()
|
||||||
{
|
{
|
||||||
AddStep("Set activity", () => api.Activity.Value = new UserActivity.Editing(new BeatmapInfo()));
|
AddStep("Set activity", () => api.Activity.Value = new UserActivity.EditingBeatmap(new BeatmapInfo()));
|
||||||
|
|
||||||
AddStep("Run command", () => Add(new NowPlayingCommand(new Channel())));
|
AddStep("Run command", () => Add(new NowPlayingCommand(new Channel())));
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -107,14 +109,16 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddStep("set online status", () => status.Value = new UserStatusOnline());
|
AddStep("set online status", () => status.Value = new UserStatusOnline());
|
||||||
|
|
||||||
AddStep("idle", () => activity.Value = null);
|
AddStep("idle", () => activity.Value = null);
|
||||||
AddStep("spectating", () => activity.Value = new UserActivity.Spectating());
|
AddStep("watching replay", () => activity.Value = new UserActivity.WatchingReplay(createScore(@"nats")));
|
||||||
|
AddStep("spectating user", () => activity.Value = new UserActivity.SpectatingUser(createScore(@"mrekk")));
|
||||||
AddStep("solo (osu!)", () => activity.Value = soloGameStatusForRuleset(0));
|
AddStep("solo (osu!)", () => activity.Value = soloGameStatusForRuleset(0));
|
||||||
AddStep("solo (osu!taiko)", () => activity.Value = soloGameStatusForRuleset(1));
|
AddStep("solo (osu!taiko)", () => activity.Value = soloGameStatusForRuleset(1));
|
||||||
AddStep("solo (osu!catch)", () => activity.Value = soloGameStatusForRuleset(2));
|
AddStep("solo (osu!catch)", () => activity.Value = soloGameStatusForRuleset(2));
|
||||||
AddStep("solo (osu!mania)", () => activity.Value = soloGameStatusForRuleset(3));
|
AddStep("solo (osu!mania)", () => activity.Value = soloGameStatusForRuleset(3));
|
||||||
AddStep("choosing", () => activity.Value = new UserActivity.ChoosingBeatmap());
|
AddStep("choosing", () => activity.Value = new UserActivity.ChoosingBeatmap());
|
||||||
AddStep("editing", () => activity.Value = new UserActivity.Editing(null));
|
AddStep("editing beatmap", () => activity.Value = new UserActivity.EditingBeatmap(null));
|
||||||
AddStep("modding", () => activity.Value = new UserActivity.Modding());
|
AddStep("modding beatmap", () => activity.Value = new UserActivity.ModdingBeatmap(null));
|
||||||
|
AddStep("testing beatmap", () => activity.Value = new UserActivity.TestingBeatmap(null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -132,6 +136,14 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(null, rulesetStore.GetRuleset(rulesetId));
|
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(null, rulesetStore.GetRuleset(rulesetId));
|
||||||
|
|
||||||
|
private ScoreInfo createScore(string name) => new ScoreInfo(new TestBeatmap(Ruleset.Value).BeatmapInfo)
|
||||||
|
{
|
||||||
|
User = new APIUser
|
||||||
|
{
|
||||||
|
Username = name,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private partial class TestUserListPanel : UserListPanel
|
private partial class TestUserListPanel : UserListPanel
|
||||||
{
|
{
|
||||||
public TestUserListPanel(APIUser user)
|
public TestUserListPanel(APIUser user)
|
||||||
|
@ -90,7 +90,9 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
||||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "mania" } },
|
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "mania" } },
|
||||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } }
|
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } },
|
||||||
|
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko", "fruits", "mania" } },
|
||||||
|
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators (Probationary)", Playmodes = new[] { "osu", "taiko", "fruits", "mania" }, IsProbationary = true }
|
||||||
},
|
},
|
||||||
ProfileOrder = new[]
|
ProfileOrder = new[]
|
||||||
{
|
{
|
||||||
@ -119,6 +121,12 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
|
Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
TournamentBanner = new TournamentBanner
|
||||||
|
{
|
||||||
|
Id = 13926,
|
||||||
|
TournamentId = 35,
|
||||||
|
ImageLowRes = "https://assets.ppy.sh/tournament-banners/official/owc2022/profile/winner_US.jpg",
|
||||||
|
},
|
||||||
Badges = new[]
|
Badges = new[]
|
||||||
{
|
{
|
||||||
new Badge
|
new Badge
|
||||||
|
@ -24,17 +24,26 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
{
|
{
|
||||||
public partial class TestSceneAccuracyCircle : OsuTestScene
|
public partial class TestSceneAccuracyCircle : OsuTestScene
|
||||||
{
|
{
|
||||||
[TestCase(0.2, ScoreRank.D)]
|
[TestCase(0)]
|
||||||
[TestCase(0.5, ScoreRank.D)]
|
[TestCase(0.2)]
|
||||||
[TestCase(0.75, ScoreRank.C)]
|
[TestCase(0.5)]
|
||||||
[TestCase(0.85, ScoreRank.B)]
|
[TestCase(0.6999)]
|
||||||
[TestCase(0.925, ScoreRank.A)]
|
[TestCase(0.7)]
|
||||||
[TestCase(0.975, ScoreRank.S)]
|
[TestCase(0.75)]
|
||||||
[TestCase(0.9999, ScoreRank.S)]
|
[TestCase(0.7999)]
|
||||||
[TestCase(1, ScoreRank.X)]
|
[TestCase(0.8)]
|
||||||
public void TestRank(double accuracy, ScoreRank rank)
|
[TestCase(0.85)]
|
||||||
|
[TestCase(0.8999)]
|
||||||
|
[TestCase(0.9)]
|
||||||
|
[TestCase(0.925)]
|
||||||
|
[TestCase(0.9499)]
|
||||||
|
[TestCase(0.95)]
|
||||||
|
[TestCase(0.975)]
|
||||||
|
[TestCase(0.9999)]
|
||||||
|
[TestCase(1)]
|
||||||
|
public void TestRank(double accuracy)
|
||||||
{
|
{
|
||||||
var score = createScore(accuracy, rank);
|
var score = createScore(accuracy, ScoreProcessor.RankFromAccuracy(accuracy));
|
||||||
|
|
||||||
addCircleStep(score);
|
addCircleStep(score);
|
||||||
}
|
}
|
||||||
|
146
osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs
Normal file
146
osu.Game.Tests/Visual/SongSelect/TestSceneSongSelectFooterV2.cs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
|
using osu.Game.Screens.Select.FooterV2;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.SongSelect
|
||||||
|
{
|
||||||
|
public partial class TestSceneSongSelectFooterV2 : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private FooterButtonRandomV2 randomButton = null!;
|
||||||
|
private FooterButtonModsV2 modsButton = null!;
|
||||||
|
|
||||||
|
private bool nextRandomCalled;
|
||||||
|
private bool previousRandomCalled;
|
||||||
|
|
||||||
|
private DummyOverlay overlay = null!;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
nextRandomCalled = false;
|
||||||
|
previousRandomCalled = false;
|
||||||
|
|
||||||
|
FooterV2 footer;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
footer = new FooterV2
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre
|
||||||
|
},
|
||||||
|
overlay = new DummyOverlay()
|
||||||
|
};
|
||||||
|
|
||||||
|
footer.AddButton(modsButton = new FooterButtonModsV2(), overlay);
|
||||||
|
footer.AddButton(randomButton = new FooterButtonRandomV2
|
||||||
|
{
|
||||||
|
NextRandom = () => nextRandomCalled = true,
|
||||||
|
PreviousRandom = () => previousRandomCalled = true
|
||||||
|
});
|
||||||
|
footer.AddButton(new FooterButtonOptionsV2());
|
||||||
|
|
||||||
|
overlay.Hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestState()
|
||||||
|
{
|
||||||
|
AddToggleStep("set options enabled state", state => this.ChildrenOfType<FooterButtonV2>().Last().Enabled.Value = state);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFooterRandom()
|
||||||
|
{
|
||||||
|
AddStep("press F2", () => InputManager.Key(Key.F2));
|
||||||
|
AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFooterRandomViaMouse()
|
||||||
|
{
|
||||||
|
AddStep("click button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(randomButton);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("next random invoked", () => nextRandomCalled && !previousRandomCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFooterRewind()
|
||||||
|
{
|
||||||
|
AddStep("press Shift+F2", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.LShift);
|
||||||
|
InputManager.PressKey(Key.F2);
|
||||||
|
InputManager.ReleaseKey(Key.F2);
|
||||||
|
InputManager.ReleaseKey(Key.LShift);
|
||||||
|
});
|
||||||
|
AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFooterRewindViaShiftMouseLeft()
|
||||||
|
{
|
||||||
|
AddStep("shift + click button", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.LShift);
|
||||||
|
InputManager.MoveMouseTo(randomButton);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
InputManager.ReleaseKey(Key.LShift);
|
||||||
|
});
|
||||||
|
AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFooterRewindViaMouseRight()
|
||||||
|
{
|
||||||
|
AddStep("right click button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(randomButton);
|
||||||
|
InputManager.Click(MouseButton.Right);
|
||||||
|
});
|
||||||
|
AddAssert("previous random invoked", () => previousRandomCalled && !nextRandomCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOverlayPresent()
|
||||||
|
{
|
||||||
|
AddStep("Press F1", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(modsButton);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("Overlay visible", () => overlay.State.Value == Visibility.Visible);
|
||||||
|
AddStep("Hide", () => overlay.Hide());
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class DummyOverlay : ShearedOverlayContainer
|
||||||
|
{
|
||||||
|
public DummyOverlay()
|
||||||
|
: base(OverlayColourScheme.Green)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Header.Title = "An overlay";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -261,7 +261,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
AddStep($"Set {name} slider to {value}", () =>
|
AddStep($"Set {name} slider to {value}", () =>
|
||||||
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
|
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
|
||||||
.ChildrenOfType<OsuSliderBar<float>>().First().Current.Value = value);
|
.ChildrenOfType<RoundedSliderBar<float>>().First().Current.Value = value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkBindableAtValue(string name, float? expectedValue)
|
private void checkBindableAtValue(string name, float? expectedValue)
|
||||||
@ -275,7 +275,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
AddAssert($"Slider {name} at {expectedValue}", () =>
|
AddAssert($"Slider {name} at {expectedValue}", () =>
|
||||||
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
|
this.ChildrenOfType<DifficultyAdjustSettingsControl>().First(c => c.LabelText == name)
|
||||||
.ChildrenOfType<OsuSliderBar<float>>().First().Current.Value == expectedValue);
|
.ChildrenOfType<RoundedSliderBar<float>>().First().Current.Value == expectedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBeatmapWithDifficultyParameters(float value)
|
private void setBeatmapWithDifficultyParameters(float value)
|
||||||
|
@ -270,7 +270,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
createScreen();
|
createScreen();
|
||||||
AddStep("select difficulty adjust via panel", () => getPanelForMod(typeof(OsuModDifficultyAdjust)).TriggerClick());
|
AddStep("select difficulty adjust via panel", () => getPanelForMod(typeof(OsuModDifficultyAdjust)).TriggerClick());
|
||||||
|
|
||||||
AddStep("set setting", () => modSelectOverlay.ChildrenOfType<OsuSliderBar<float>>().First().Current.Value = 8);
|
AddStep("set setting", () => modSelectOverlay.ChildrenOfType<RoundedSliderBar<float>>().First().Current.Value = 8);
|
||||||
|
|
||||||
AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType<OsuModDifficultyAdjust>().Single().CircleSize.Value == 8);
|
AddAssert("ensure setting is propagated", () => SelectedMods.Value.OfType<OsuModDifficultyAdjust>().Single().CircleSize.Value == 8);
|
||||||
|
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
public partial class TestSceneShearedSliderBar : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||||
|
|
||||||
|
private readonly BindableDouble current = new BindableDouble(5)
|
||||||
|
{
|
||||||
|
Precision = 0.1f,
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Child = new ShearedSliderBar<double>
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Current = current,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Width = 0.4f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,11 +2,11 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="DeepEqual" Version="4.2.1" />
|
<PackageReference Include="DeepEqual" Version="4.2.1" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
<PackageReference Include="Moq" Version="4.18.2" />
|
<PackageReference Include="Moq" Version="4.18.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
<StartupObject>osu.Game.Tournament.Tests.TournamentTestRunner</StartupObject>
|
<StartupObject>osu.Game.Tournament.Tests.TournamentTestRunner</StartupObject>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -98,6 +98,9 @@ namespace osu.Game.Audio
|
|||||||
|
|
||||||
Track.Stop();
|
Track.Stop();
|
||||||
|
|
||||||
|
// Ensure the track is reset immediately on stopping, so the next time it is started it has a correct time value.
|
||||||
|
Track.Seek(0);
|
||||||
|
|
||||||
Stopped?.Invoke();
|
Stopped?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,10 +66,16 @@ namespace osu.Game.Extensions
|
|||||||
|
|
||||||
foreach (var (_, property) in component.GetSettingsSourceProperties())
|
foreach (var (_, property) in component.GetSettingsSourceProperties())
|
||||||
{
|
{
|
||||||
if (!info.Settings.TryGetValue(property.Name.ToSnakeCase(), out object? settingValue))
|
var bindable = ((IBindable)property.GetValue(component)!);
|
||||||
continue;
|
|
||||||
|
|
||||||
skinnable.CopyAdjustedSetting(((IBindable)property.GetValue(component)!), settingValue);
|
if (!info.Settings.TryGetValue(property.Name.ToSnakeCase(), out object? settingValue))
|
||||||
|
{
|
||||||
|
// TODO: We probably want to restore default if not included in serialisation information.
|
||||||
|
// This is not simple to do as SetDefault() is only found in the typed Bindable<T> interface right now.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
skinnable.CopyAdjustedSetting(bindable, settingValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,9 +100,9 @@ namespace osu.Game.Graphics.Containers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Abort any ongoing confirmation. Should be called when the container's interaction is no longer valid (ie. the user releases a key).
|
/// Abort any ongoing confirmation. Should be called when the container's interaction is no longer valid (ie. the user releases a key).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void AbortConfirm()
|
protected virtual void AbortConfirm()
|
||||||
{
|
{
|
||||||
if (!AllowMultipleFires && Fired) return;
|
if (!confirming || (!AllowMultipleFires && Fired)) return;
|
||||||
|
|
||||||
confirming = false;
|
confirming = false;
|
||||||
Fired = false;
|
Fired = false;
|
||||||
|
@ -46,8 +46,8 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
|
CreateHoverSounds(sampleSet),
|
||||||
content,
|
content,
|
||||||
CreateHoverSounds(sampleSet)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ExpandableSlider<T, TSlider> : CompositeDrawable, IExpandable, IHasCurrentValue<T>
|
public partial class ExpandableSlider<T, TSlider> : CompositeDrawable, IExpandable, IHasCurrentValue<T>
|
||||||
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
||||||
where TSlider : OsuSliderBar<T>, new()
|
where TSlider : RoundedSliderBar<T>, new()
|
||||||
{
|
{
|
||||||
private readonly OsuSpriteText label;
|
private readonly OsuSpriteText label;
|
||||||
private readonly TSlider slider;
|
private readonly TSlider slider;
|
||||||
@ -130,7 +130,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// An <see cref="IExpandable"/> implementation for the UI slider bar control.
|
/// An <see cref="IExpandable"/> implementation for the UI slider bar control.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ExpandableSlider<T> : ExpandableSlider<T, OsuSliderBar<T>>
|
public partial class ExpandableSlider<T> : ExpandableSlider<T, RoundedSliderBar<T>>
|
||||||
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -58,7 +55,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load([CanBeNull] OverlayColourProvider colourProvider, OsuColour colours)
|
private void load(OverlayColourProvider? colourProvider, OsuColour colours)
|
||||||
{
|
{
|
||||||
AccentColour = colourProvider?.Highlight1 ?? colours.Pink;
|
AccentColour = colourProvider?.Highlight1 ?? colours.Pink;
|
||||||
GlowingAccentColour = colourProvider?.Highlight1.Lighten(0.2f) ?? colours.PinkLighter;
|
GlowingAccentColour = colourProvider?.Highlight1.Lighten(0.2f) ?? colours.PinkLighter;
|
||||||
|
@ -1,195 +1,59 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
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;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.UserInterface;
|
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Overlays;
|
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public partial class OsuSliderBar<T> : SliderBar<T>, IHasTooltip, IHasAccentColour
|
public abstract partial class OsuSliderBar<T> : SliderBar<T>, IHasTooltip
|
||||||
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Maximum number of decimal digits to be displayed in the tooltip.
|
|
||||||
/// </summary>
|
|
||||||
private const int max_decimal_digits = 5;
|
|
||||||
|
|
||||||
private Sample sample;
|
|
||||||
private double lastSampleTime;
|
|
||||||
private T lastSampleValue;
|
|
||||||
|
|
||||||
protected readonly Nub Nub;
|
|
||||||
protected readonly Box LeftBox;
|
|
||||||
protected readonly Box RightBox;
|
|
||||||
private readonly Container nubContainer;
|
|
||||||
|
|
||||||
public virtual LocalisableString TooltipText { get; private set; }
|
|
||||||
|
|
||||||
public bool PlaySamplesOnAdjust { get; set; } = true;
|
public bool PlaySamplesOnAdjust { get; set; } = true;
|
||||||
|
|
||||||
private readonly HoverClickSounds hoverClickSounds;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to format the tooltip as a percentage or the actual value.
|
/// Whether to format the tooltip as a percentage or the actual value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool DisplayAsPercentage { get; set; }
|
public bool DisplayAsPercentage { get; set; }
|
||||||
|
|
||||||
private Color4 accentColour;
|
public virtual LocalisableString TooltipText { get; private set; }
|
||||||
|
|
||||||
public Color4 AccentColour
|
/// <summary>
|
||||||
{
|
/// Maximum number of decimal digits to be displayed in the tooltip.
|
||||||
get => accentColour;
|
/// </summary>
|
||||||
set
|
private const int max_decimal_digits = 5;
|
||||||
{
|
|
||||||
accentColour = value;
|
|
||||||
LeftBox.Colour = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Colour4 backgroundColour;
|
private Sample sample = null!;
|
||||||
|
|
||||||
public Color4 BackgroundColour
|
private double lastSampleTime;
|
||||||
{
|
private T lastSampleValue;
|
||||||
get => backgroundColour;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
backgroundColour = value;
|
|
||||||
RightBox.Colour = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsuSliderBar()
|
[BackgroundDependencyLoader]
|
||||||
{
|
private void load(AudioManager audio)
|
||||||
Height = Nub.HEIGHT;
|
|
||||||
RangePadding = Nub.EXPANDED_SIZE / 2;
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Padding = new MarginPadding { Horizontal = 2 },
|
|
||||||
Child = new CircularContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Masking = true,
|
|
||||||
CornerRadius = 5f,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
LeftBox = new Box
|
|
||||||
{
|
|
||||||
Height = 5,
|
|
||||||
EdgeSmoothness = new Vector2(0, 0.5f),
|
|
||||||
RelativeSizeAxes = Axes.None,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
},
|
|
||||||
RightBox = new Box
|
|
||||||
{
|
|
||||||
Height = 5,
|
|
||||||
EdgeSmoothness = new Vector2(0, 0.5f),
|
|
||||||
RelativeSizeAxes = Axes.None,
|
|
||||||
Anchor = Anchor.CentreRight,
|
|
||||||
Origin = Anchor.CentreRight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nubContainer = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Child = Nub = new Nub
|
|
||||||
{
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
RelativePositionAxes = Axes.X,
|
|
||||||
Current = { Value = true }
|
|
||||||
},
|
|
||||||
},
|
|
||||||
hoverClickSounds = new HoverClickSounds()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
|
||||||
private void load(AudioManager audio, [CanBeNull] OverlayColourProvider colourProvider, OsuColour colours)
|
|
||||||
{
|
{
|
||||||
sample = audio.Samples.Get(@"UI/notch-tick");
|
sample = audio.Samples.Get(@"UI/notch-tick");
|
||||||
AccentColour = colourProvider?.Highlight1 ?? colours.Pink;
|
|
||||||
BackgroundColour = colourProvider?.Background5 ?? colours.PinkDarker.Darken(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
nubContainer.Padding = new MarginPadding { Horizontal = RangePadding };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
CurrentNumber.BindValueChanged(current => TooltipText = getTooltipText(current.NewValue), true);
|
CurrentNumber.BindValueChanged(current => TooltipText = getTooltipText(current.NewValue), true);
|
||||||
|
|
||||||
Current.BindDisabledChanged(disabled =>
|
|
||||||
{
|
|
||||||
Alpha = disabled ? 0.3f : 1;
|
|
||||||
hoverClickSounds.Enabled.Value = !disabled;
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
|
||||||
{
|
|
||||||
updateGlow();
|
|
||||||
return base.OnHover(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnHoverLost(HoverLostEvent e)
|
|
||||||
{
|
|
||||||
updateGlow();
|
|
||||||
base.OnHoverLost(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool ShouldHandleAsRelativeDrag(MouseDownEvent e)
|
|
||||||
=> Nub.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition);
|
|
||||||
|
|
||||||
protected override void OnDragEnd(DragEndEvent e)
|
|
||||||
{
|
|
||||||
updateGlow();
|
|
||||||
base.OnDragEnd(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateGlow()
|
|
||||||
{
|
|
||||||
Nub.Glowing = !Current.Disabled && (IsHovered || IsDragged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnUserChange(T value)
|
protected override void OnUserChange(T value)
|
||||||
{
|
{
|
||||||
base.OnUserChange(value);
|
base.OnUserChange(value);
|
||||||
|
|
||||||
playSample(value);
|
playSample(value);
|
||||||
|
|
||||||
TooltipText = getTooltipText(value);
|
TooltipText = getTooltipText(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,18 +100,6 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
return floatValue.ToString($"N{significantDigits}");
|
return floatValue.ToString($"N{significantDigits}");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateAfterChildren()
|
|
||||||
{
|
|
||||||
base.UpdateAfterChildren();
|
|
||||||
LeftBox.Scale = new Vector2(Math.Clamp(RangePadding + Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, Math.Max(0, DrawWidth)), 1);
|
|
||||||
RightBox.Scale = new Vector2(Math.Clamp(DrawWidth - Nub.DrawPosition.X - RangePadding - Nub.DrawWidth / 2, 0, Math.Max(0, DrawWidth)), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateValue(float value)
|
|
||||||
{
|
|
||||||
Nub.MoveToX(value, 250, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes all non-significant digits, keeping at most a requested number of decimal digits.
|
/// Removes all non-significant digits, keeping at most a requested number of decimal digits.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -158,7 +158,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
&& screenSpacePos.X >= Nub.ScreenSpaceDrawQuad.TopLeft.X;
|
&& screenSpacePos.X >= Nub.ScreenSpaceDrawQuad.TopLeft.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected partial class BoundSlider : OsuSliderBar<double>
|
protected partial class BoundSlider : RoundedSliderBar<double>
|
||||||
{
|
{
|
||||||
public string? DefaultString;
|
public string? DefaultString;
|
||||||
public LocalisableString? DefaultTooltip;
|
public LocalisableString? DefaultTooltip;
|
||||||
|
170
osu.Game/Graphics/UserInterface/RoundedSliderBar.cs
Normal file
170
osu.Game/Graphics/UserInterface/RoundedSliderBar.cs
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
public partial class RoundedSliderBar<T> : OsuSliderBar<T>
|
||||||
|
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
||||||
|
{
|
||||||
|
protected readonly Nub Nub;
|
||||||
|
protected readonly Box LeftBox;
|
||||||
|
protected readonly Box RightBox;
|
||||||
|
private readonly Container nubContainer;
|
||||||
|
|
||||||
|
private readonly HoverClickSounds hoverClickSounds;
|
||||||
|
|
||||||
|
private Color4 accentColour;
|
||||||
|
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get => accentColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
accentColour = value;
|
||||||
|
LeftBox.Colour = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Colour4 backgroundColour;
|
||||||
|
|
||||||
|
public Color4 BackgroundColour
|
||||||
|
{
|
||||||
|
get => backgroundColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
backgroundColour = value;
|
||||||
|
RightBox.Colour = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoundedSliderBar()
|
||||||
|
{
|
||||||
|
Height = Nub.HEIGHT;
|
||||||
|
RangePadding = Nub.EXPANDED_SIZE / 2;
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Padding = new MarginPadding { Horizontal = 2 },
|
||||||
|
Child = new CircularContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 5f,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
LeftBox = new Box
|
||||||
|
{
|
||||||
|
Height = 5,
|
||||||
|
EdgeSmoothness = new Vector2(0, 0.5f),
|
||||||
|
RelativeSizeAxes = Axes.None,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
},
|
||||||
|
RightBox = new Box
|
||||||
|
{
|
||||||
|
Height = 5,
|
||||||
|
EdgeSmoothness = new Vector2(0, 0.5f),
|
||||||
|
RelativeSizeAxes = Axes.None,
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nubContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = Nub = new Nub
|
||||||
|
{
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
RelativePositionAxes = Axes.X,
|
||||||
|
Current = { Value = true }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hoverClickSounds = new HoverClickSounds()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(OverlayColourProvider? colourProvider, OsuColour colours)
|
||||||
|
{
|
||||||
|
AccentColour = colourProvider?.Highlight1 ?? colours.Pink;
|
||||||
|
BackgroundColour = colourProvider?.Background5 ?? colours.PinkDarker.Darken(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
nubContainer.Padding = new MarginPadding { Horizontal = RangePadding };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Current.BindDisabledChanged(disabled =>
|
||||||
|
{
|
||||||
|
Alpha = disabled ? 0.3f : 1;
|
||||||
|
hoverClickSounds.Enabled.Value = !disabled;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
updateGlow();
|
||||||
|
return base.OnHover(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
updateGlow();
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool ShouldHandleAsRelativeDrag(MouseDownEvent e)
|
||||||
|
=> Nub.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition);
|
||||||
|
|
||||||
|
protected override void OnDragEnd(DragEndEvent e)
|
||||||
|
{
|
||||||
|
updateGlow();
|
||||||
|
base.OnDragEnd(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateGlow()
|
||||||
|
{
|
||||||
|
Nub.Glowing = !Current.Disabled && (IsHovered || IsDragged);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateAfterChildren()
|
||||||
|
{
|
||||||
|
base.UpdateAfterChildren();
|
||||||
|
LeftBox.Scale = new Vector2(Math.Clamp(RangePadding + Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, Math.Max(0, DrawWidth)), 1);
|
||||||
|
RightBox.Scale = new Vector2(Math.Clamp(DrawWidth - Nub.DrawPosition.X - RangePadding - Nub.DrawWidth / 2, 0, Math.Max(0, DrawWidth)), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateValue(float value)
|
||||||
|
{
|
||||||
|
Nub.MoveToX(value, 250, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
183
osu.Game/Graphics/UserInterface/ShearedNub.cs
Normal file
183
osu.Game/Graphics/UserInterface/ShearedNub.cs
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
public partial class ShearedNub : Container, IHasCurrentValue<bool>, IHasAccentColour
|
||||||
|
{
|
||||||
|
protected const float BORDER_WIDTH = 3;
|
||||||
|
|
||||||
|
public const int HEIGHT = 30;
|
||||||
|
public const float EXPANDED_SIZE = 50;
|
||||||
|
|
||||||
|
public static readonly Vector2 SHEAR = new Vector2(0.15f, 0);
|
||||||
|
|
||||||
|
private readonly Box fill;
|
||||||
|
private readonly Container main;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implements the shape for the nub, allowing for any type of container to be used.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public ShearedNub()
|
||||||
|
{
|
||||||
|
Size = new Vector2(EXPANDED_SIZE, HEIGHT);
|
||||||
|
InternalChild = main = new Container
|
||||||
|
{
|
||||||
|
Shear = SHEAR,
|
||||||
|
BorderColour = Colour4.White,
|
||||||
|
BorderThickness = BORDER_WIDTH,
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 5,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Child = fill = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
AlwaysPresent = true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(OverlayColourProvider? colourProvider, OsuColour colours)
|
||||||
|
{
|
||||||
|
AccentColour = colourProvider?.Highlight1 ?? colours.Pink;
|
||||||
|
GlowingAccentColour = colourProvider?.Highlight1.Lighten(0.4f) ?? colours.PinkLighter;
|
||||||
|
GlowColour = colourProvider?.Highlight1 ?? colours.PinkLighter;
|
||||||
|
|
||||||
|
main.EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Colour = GlowColour.Opacity(0),
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Radius = 8,
|
||||||
|
Roundness = 4,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Current.BindValueChanged(onCurrentValueChanged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool glowing;
|
||||||
|
|
||||||
|
public bool Glowing
|
||||||
|
{
|
||||||
|
get => glowing;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (glowing == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
glowing = value;
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
main.FadeColour(GlowingAccentColour.Lighten(0.1f), 40, Easing.OutQuint)
|
||||||
|
.Then()
|
||||||
|
.FadeColour(GlowingAccentColour, 800, Easing.OutQuint);
|
||||||
|
|
||||||
|
main.FadeEdgeEffectTo(Color4.White.Opacity(0.1f), 40, Easing.OutQuint)
|
||||||
|
.Then()
|
||||||
|
.FadeEdgeEffectTo(GlowColour.Opacity(0.1f), 800, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
main.FadeEdgeEffectTo(GlowColour.Opacity(0), 800, Easing.OutQuint);
|
||||||
|
main.FadeColour(AccentColour, 800, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Bindable<bool> current = new Bindable<bool>();
|
||||||
|
|
||||||
|
public Bindable<bool> Current
|
||||||
|
{
|
||||||
|
get => current;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(value);
|
||||||
|
|
||||||
|
current.UnbindBindings();
|
||||||
|
current.BindTo(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 accentColour;
|
||||||
|
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get => accentColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
accentColour = value;
|
||||||
|
if (!Glowing)
|
||||||
|
main.Colour = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 glowingAccentColour;
|
||||||
|
|
||||||
|
public Color4 GlowingAccentColour
|
||||||
|
{
|
||||||
|
get => glowingAccentColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
glowingAccentColour = value;
|
||||||
|
if (Glowing)
|
||||||
|
main.Colour = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 glowColour;
|
||||||
|
|
||||||
|
public Color4 GlowColour
|
||||||
|
{
|
||||||
|
get => glowColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
glowColour = value;
|
||||||
|
|
||||||
|
var effect = main.EdgeEffect;
|
||||||
|
effect.Colour = Glowing ? value : value.Opacity(0);
|
||||||
|
main.EdgeEffect = effect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onCurrentValueChanged(ValueChangedEvent<bool> filled)
|
||||||
|
{
|
||||||
|
const double duration = 200;
|
||||||
|
|
||||||
|
fill.FadeTo(filled.NewValue ? 1 : 0, duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
if (filled.NewValue)
|
||||||
|
{
|
||||||
|
main.ResizeWidthTo(1, duration, Easing.OutElasticHalf);
|
||||||
|
main.TransformTo(nameof(BorderThickness), 8.5f, duration, Easing.OutElasticHalf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
main.ResizeWidthTo(0.75f, duration, Easing.OutQuint);
|
||||||
|
main.TransformTo(nameof(BorderThickness), BORDER_WIDTH, duration, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
173
osu.Game/Graphics/UserInterface/ShearedSliderBar.cs
Normal file
173
osu.Game/Graphics/UserInterface/ShearedSliderBar.cs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using static osu.Game.Graphics.UserInterface.ShearedNub;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
public partial class ShearedSliderBar<T> : OsuSliderBar<T>
|
||||||
|
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
||||||
|
{
|
||||||
|
protected readonly ShearedNub Nub;
|
||||||
|
protected readonly Box LeftBox;
|
||||||
|
protected readonly Box RightBox;
|
||||||
|
private readonly Container nubContainer;
|
||||||
|
|
||||||
|
private readonly HoverClickSounds hoverClickSounds;
|
||||||
|
|
||||||
|
private Color4 accentColour;
|
||||||
|
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get => accentColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
accentColour = value;
|
||||||
|
|
||||||
|
// We want to slightly darken the colour for the box because the sheared slider has the boxes at the same height as the nub,
|
||||||
|
// making the nub invisible when not hovered.
|
||||||
|
LeftBox.Colour = value.Darken(0.1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Colour4 backgroundColour;
|
||||||
|
|
||||||
|
public Color4 BackgroundColour
|
||||||
|
{
|
||||||
|
get => backgroundColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
backgroundColour = value;
|
||||||
|
RightBox.Colour = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShearedSliderBar()
|
||||||
|
{
|
||||||
|
Shear = SHEAR;
|
||||||
|
Height = HEIGHT;
|
||||||
|
RangePadding = EXPANDED_SIZE / 2;
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Padding = new MarginPadding { Horizontal = 2 },
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 5,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
LeftBox = new Box
|
||||||
|
{
|
||||||
|
EdgeSmoothness = new Vector2(0, 0.5f),
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
},
|
||||||
|
RightBox = new Box
|
||||||
|
{
|
||||||
|
EdgeSmoothness = new Vector2(0, 0.5f),
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nubContainer = new Container
|
||||||
|
{
|
||||||
|
Shear = -SHEAR,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = Nub = new ShearedNub
|
||||||
|
{
|
||||||
|
X = -SHEAR.X * HEIGHT / 2f,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
RelativePositionAxes = Axes.X,
|
||||||
|
Current = { Value = true }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hoverClickSounds = new HoverClickSounds()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(OverlayColourProvider? colourProvider, OsuColour colours)
|
||||||
|
{
|
||||||
|
AccentColour = colourProvider?.Highlight1 ?? colours.Pink;
|
||||||
|
BackgroundColour = colourProvider?.Background5 ?? colours.PinkDarker.Darken(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
nubContainer.Padding = new MarginPadding { Horizontal = RangePadding };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Current.BindDisabledChanged(disabled =>
|
||||||
|
{
|
||||||
|
Alpha = disabled ? 0.3f : 1;
|
||||||
|
hoverClickSounds.Enabled.Value = !disabled;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
updateGlow();
|
||||||
|
return base.OnHover(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
updateGlow();
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool ShouldHandleAsRelativeDrag(MouseDownEvent e)
|
||||||
|
=> Nub.ReceivePositionalInputAt(e.ScreenSpaceMouseDownPosition);
|
||||||
|
|
||||||
|
protected override void OnDragEnd(DragEndEvent e)
|
||||||
|
{
|
||||||
|
updateGlow();
|
||||||
|
base.OnDragEnd(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateGlow()
|
||||||
|
{
|
||||||
|
Nub.Glowing = !Current.Disabled && (IsHovered || IsDragged);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateAfterChildren()
|
||||||
|
{
|
||||||
|
base.UpdateAfterChildren();
|
||||||
|
LeftBox.Scale = new Vector2(Math.Clamp(RangePadding + Nub.DrawPosition.X - Nub.DrawWidth / 2.15f, 0, Math.Max(0, DrawWidth)), 1);
|
||||||
|
RightBox.Scale = new Vector2(Math.Clamp(DrawWidth - Nub.DrawPosition.X - RangePadding - Nub.DrawWidth / 2.15f, 0, Math.Max(0, DrawWidth)), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateValue(float value)
|
||||||
|
{
|
||||||
|
Nub.MoveToX(value, 250, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A slider bar which displays a millisecond time value.
|
/// A slider bar which displays a millisecond time value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class TimeSlider : OsuSliderBar<double>
|
public partial class TimeSlider : RoundedSliderBar<double>
|
||||||
{
|
{
|
||||||
public override LocalisableString TooltipText => $"{Current.Value:N0} ms";
|
public override LocalisableString TooltipText => $"{Current.Value:N0} ms";
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,8 @@ namespace osu.Game.Input.Bindings
|
|||||||
// It is used to decide the order of precedence, with the earlier items having higher precedence.
|
// It is used to decide the order of precedence, with the earlier items having higher precedence.
|
||||||
public override IEnumerable<IKeyBinding> DefaultKeyBindings => GlobalKeyBindings
|
public override IEnumerable<IKeyBinding> DefaultKeyBindings => GlobalKeyBindings
|
||||||
.Concat(EditorKeyBindings)
|
.Concat(EditorKeyBindings)
|
||||||
.Concat(ReplayKeyBindings)
|
|
||||||
.Concat(InGameKeyBindings)
|
.Concat(InGameKeyBindings)
|
||||||
|
.Concat(ReplayKeyBindings)
|
||||||
.Concat(SongSelectKeyBindings)
|
.Concat(SongSelectKeyBindings)
|
||||||
.Concat(AudioControlKeyBindings)
|
.Concat(AudioControlKeyBindings)
|
||||||
// Overlay bindings may conflict with more local cases like the editor so they are checked last.
|
// Overlay bindings may conflict with more local cases like the editor so they are checked last.
|
||||||
|
@ -234,6 +234,10 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
set => Statistics.RankHistory = value;
|
set => Statistics.RankHistory = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonProperty(@"active_tournament_banner")]
|
||||||
|
[CanBeNull]
|
||||||
|
public TournamentBanner TournamentBanner;
|
||||||
|
|
||||||
[JsonProperty("badges")]
|
[JsonProperty("badges")]
|
||||||
public Badge[] Badges;
|
public Badge[] Badges;
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ namespace osu.Game.Online.Chat
|
|||||||
{
|
{
|
||||||
connector.ChannelJoined += ch => Schedule(() => joinChannel(ch));
|
connector.ChannelJoined += ch => Schedule(() => joinChannel(ch));
|
||||||
|
|
||||||
connector.ChannelParted += ch => Schedule(() => LeaveChannel(getChannel(ch)));
|
connector.ChannelParted += ch => Schedule(() => leaveChannel(getChannel(ch), false));
|
||||||
|
|
||||||
connector.NewMessages += msgs => Schedule(() => addMessages(msgs));
|
connector.NewMessages += msgs => Schedule(() => addMessages(msgs));
|
||||||
|
|
||||||
@ -558,7 +558,9 @@ namespace osu.Game.Online.Chat
|
|||||||
/// Leave the specified channel. Can be called from any thread.
|
/// Leave the specified channel. Can be called from any thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channel">The channel to leave.</param>
|
/// <param name="channel">The channel to leave.</param>
|
||||||
public void LeaveChannel(Channel channel) => Schedule(() =>
|
public void LeaveChannel(Channel channel) => Schedule(() => leaveChannel(channel, true));
|
||||||
|
|
||||||
|
private void leaveChannel(Channel channel, bool sendLeaveRequest)
|
||||||
{
|
{
|
||||||
if (channel == null) return;
|
if (channel == null) return;
|
||||||
|
|
||||||
@ -581,10 +583,11 @@ namespace osu.Game.Online.Chat
|
|||||||
|
|
||||||
if (channel.Joined.Value)
|
if (channel.Joined.Value)
|
||||||
{
|
{
|
||||||
api.Queue(new LeaveChannelRequest(channel));
|
if (sendLeaveRequest)
|
||||||
|
api.Queue(new LeaveChannelRequest(channel));
|
||||||
channel.Joined.Value = false;
|
channel.Joined.Value = false;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens the most recently closed channel that has not already been reopened,
|
/// Opens the most recently closed channel that has not already been reopened,
|
||||||
|
@ -61,7 +61,7 @@ namespace osu.Game.Online.Chat
|
|||||||
beatmapInfo = game.BeatmapInfo;
|
beatmapInfo = game.BeatmapInfo;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UserActivity.Editing edit:
|
case UserActivity.EditingBeatmap edit:
|
||||||
verb = "editing";
|
verb = "editing";
|
||||||
beatmapInfo = edit.BeatmapInfo;
|
beatmapInfo = edit.BeatmapInfo;
|
||||||
break;
|
break;
|
||||||
|
@ -60,6 +60,7 @@ using osu.Game.Screens.Menu;
|
|||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
@ -501,6 +502,23 @@ namespace osu.Game
|
|||||||
/// <param name="version">The build version of the update stream</param>
|
/// <param name="version">The build version of the update stream</param>
|
||||||
public void ShowChangelogBuild(string updateStream, string version) => waitForReady(() => changelogOverlay, _ => changelogOverlay.ShowBuild(updateStream, version));
|
public void ShowChangelogBuild(string updateStream, string version) => waitForReady(() => changelogOverlay, _ => changelogOverlay.ShowBuild(updateStream, version));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Present a skin select immediately.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="skin">The skin to select.</param>
|
||||||
|
public void PresentSkin(SkinInfo skin)
|
||||||
|
{
|
||||||
|
var databasedSkin = SkinManager.Query(s => s.ID == skin.ID);
|
||||||
|
|
||||||
|
if (databasedSkin == null)
|
||||||
|
{
|
||||||
|
Logger.Log("The requested skin could not be loaded.", LoggingTarget.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkinManager.CurrentSkinInfo.Value = databasedSkin;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Present a beatmap at song select immediately.
|
/// Present a beatmap at song select immediately.
|
||||||
/// The user should have already requested this interactively.
|
/// The user should have already requested this interactively.
|
||||||
@ -777,6 +795,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
// todo: all archive managers should be able to be looped here.
|
// todo: all archive managers should be able to be looped here.
|
||||||
SkinManager.PostNotification = n => Notifications.Post(n);
|
SkinManager.PostNotification = n => Notifications.Post(n);
|
||||||
|
SkinManager.PresentImport = items => PresentSkin(items.First().Value);
|
||||||
|
|
||||||
BeatmapManager.PostNotification = n => Notifications.Post(n);
|
BeatmapManager.PostNotification = n => Notifications.Post(n);
|
||||||
BeatmapManager.PresentImport = items => PresentBeatmap(items.First().Value);
|
BeatmapManager.PresentImport = items => PresentBeatmap(items.First().Value);
|
||||||
|
@ -315,10 +315,10 @@ namespace osu.Game.Overlays
|
|||||||
channelListing.Hide();
|
channelListing.Hide();
|
||||||
textBar.ShowSearch.Value = false;
|
textBar.ShowSearch.Value = false;
|
||||||
|
|
||||||
if (loadedChannels.ContainsKey(newChannel))
|
if (loadedChannels.TryGetValue(newChannel, out var loadedChannel))
|
||||||
{
|
{
|
||||||
currentChannelContainer.Clear(false);
|
currentChannelContainer.Clear(false);
|
||||||
currentChannelContainer.Add(loadedChannels[newChannel]);
|
currentChannelContainer.Add(loadedChannel);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -76,6 +76,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
private GridContainer content = null!;
|
private GridContainer content = null!;
|
||||||
private VotePill votePill = null!;
|
private VotePill votePill = null!;
|
||||||
private Container<CommentEditor> replyEditorContainer = null!;
|
private Container<CommentEditor> replyEditorContainer = null!;
|
||||||
|
private Container repliesButtonContainer = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IDialogOverlay? dialogOverlay { get; set; }
|
private IDialogOverlay? dialogOverlay { get; set; }
|
||||||
@ -239,10 +240,12 @@ namespace osu.Game.Overlays.Comments
|
|||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Padding = new MarginPadding { Top = 10 },
|
Padding = new MarginPadding { Top = 10 },
|
||||||
|
Alpha = 0,
|
||||||
},
|
},
|
||||||
new Container
|
repliesButtonContainer = new Container
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
showRepliesButton = new ShowRepliesButton(Comment.RepliesCount)
|
showRepliesButton = new ShowRepliesButton(Comment.RepliesCount)
|
||||||
@ -449,6 +452,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
{
|
{
|
||||||
if (replyEditorContainer.Count == 0)
|
if (replyEditorContainer.Count == 0)
|
||||||
{
|
{
|
||||||
|
replyEditorContainer.Show();
|
||||||
replyEditorContainer.Add(new ReplyCommentEditor(Comment)
|
replyEditorContainer.Add(new ReplyCommentEditor(Comment)
|
||||||
{
|
{
|
||||||
OnPost = comments =>
|
OnPost = comments =>
|
||||||
@ -456,12 +460,14 @@ namespace osu.Game.Overlays.Comments
|
|||||||
Comment.RepliesCount += comments.Length;
|
Comment.RepliesCount += comments.Length;
|
||||||
showRepliesButton.Count = Comment.RepliesCount;
|
showRepliesButton.Count = Comment.RepliesCount;
|
||||||
Replies.AddRange(comments);
|
Replies.AddRange(comments);
|
||||||
}
|
},
|
||||||
|
OnCancel = toggleReply
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
replyEditorContainer.Clear(true);
|
replyEditorContainer.ForEach(e => e.Expire());
|
||||||
|
replyEditorContainer.Hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,9 +519,11 @@ namespace osu.Game.Overlays.Comments
|
|||||||
int loadedRepliesCount = loadedReplies.Count;
|
int loadedRepliesCount = loadedReplies.Count;
|
||||||
bool hasUnloadedReplies = loadedRepliesCount != Comment.RepliesCount;
|
bool hasUnloadedReplies = loadedRepliesCount != Comment.RepliesCount;
|
||||||
|
|
||||||
loadRepliesButton.FadeTo(hasUnloadedReplies && loadedRepliesCount == 0 ? 1 : 0);
|
|
||||||
showMoreButton.FadeTo(hasUnloadedReplies && loadedRepliesCount > 0 ? 1 : 0);
|
|
||||||
showRepliesButton.FadeTo(loadedRepliesCount != 0 ? 1 : 0);
|
showRepliesButton.FadeTo(loadedRepliesCount != 0 ? 1 : 0);
|
||||||
|
loadRepliesButton.FadeTo(hasUnloadedReplies && loadedRepliesCount == 0 ? 1 : 0);
|
||||||
|
repliesButtonContainer.FadeTo(repliesButtonContainer.Any(child => child.Alpha > 0) ? 1 : 0);
|
||||||
|
|
||||||
|
showMoreButton.FadeTo(hasUnloadedReplies && loadedRepliesCount > 0 ? 1 : 0);
|
||||||
|
|
||||||
if (Comment.IsTopLevel)
|
if (Comment.IsTopLevel)
|
||||||
chevronButton.FadeTo(loadedRepliesCount != 0 ? 1 : 0);
|
chevronButton.FadeTo(loadedRepliesCount != 0 ? 1 : 0);
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -33,7 +32,6 @@ namespace osu.Game.Overlays.Comments
|
|||||||
public ReplyCommentEditor(Comment parent)
|
public ReplyCommentEditor(Comment parent)
|
||||||
{
|
{
|
||||||
parentComment = parent;
|
parentComment = parent;
|
||||||
OnCancel = () => this.FadeOut(200).Expire();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -57,6 +57,7 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
private Sample confirmSample;
|
private Sample confirmSample;
|
||||||
private double lastTickPlaybackTime;
|
private double lastTickPlaybackTime;
|
||||||
private AudioFilter lowPassFilter = null!;
|
private AudioFilter lowPassFilter = null!;
|
||||||
|
private bool mouseDown;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
@ -73,6 +74,12 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
Progress.BindValueChanged(progressChanged);
|
Progress.BindValueChanged(progressChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void AbortConfirm()
|
||||||
|
{
|
||||||
|
lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF);
|
||||||
|
base.AbortConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Confirm()
|
protected override void Confirm()
|
||||||
{
|
{
|
||||||
lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF);
|
lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF);
|
||||||
@ -83,6 +90,7 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
BeginConfirm();
|
BeginConfirm();
|
||||||
|
mouseDown = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,11 +98,28 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
{
|
{
|
||||||
if (!e.HasAnyButtonPressed)
|
if (!e.HasAnyButtonPressed)
|
||||||
{
|
{
|
||||||
lowPassFilter.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF);
|
|
||||||
AbortConfirm();
|
AbortConfirm();
|
||||||
|
mouseDown = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
if (mouseDown)
|
||||||
|
BeginConfirm();
|
||||||
|
|
||||||
|
return base.OnHover(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
|
||||||
|
if (!mouseDown) return;
|
||||||
|
|
||||||
|
AbortConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
private void progressChanged(ValueChangedEvent<double> progress)
|
private void progressChanged(ValueChangedEvent<double> progress)
|
||||||
{
|
{
|
||||||
if (progress.NewValue < progress.OldValue) return;
|
if (progress.NewValue < progress.OldValue) return;
|
||||||
|
@ -107,7 +107,7 @@ namespace osu.Game.Overlays.FirstRunSetup
|
|||||||
public override bool? AllowTrackAdjustments => false;
|
public override bool? AllowTrackAdjustments => false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class UIScaleSlider : OsuSliderBar<float>
|
private partial class UIScaleSlider : RoundedSliderBar<float>
|
||||||
{
|
{
|
||||||
public override LocalisableString TooltipText => base.TooltipText + "x";
|
public override LocalisableString TooltipText => base.TooltipText + "x";
|
||||||
}
|
}
|
||||||
|
63
osu.Game/Overlays/Profile/Header/BannerHeaderContainer.cs
Normal file
63
osu.Game/Overlays/Profile/Header/BannerHeaderContainer.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Threading;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Overlays.Profile.Header.Components;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Profile.Header
|
||||||
|
{
|
||||||
|
public partial class BannerHeaderContainer : CompositeDrawable
|
||||||
|
{
|
||||||
|
public readonly Bindable<UserProfileData?> User = new Bindable<UserProfileData?>();
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Alpha = 0;
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
FillMode = FillMode.Fit;
|
||||||
|
FillAspectRatio = 1000 / 60f;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
User.BindValueChanged(u => updateDisplay(u.NewValue?.User), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CancellationTokenSource? cancellationTokenSource;
|
||||||
|
|
||||||
|
private void updateDisplay(APIUser? user)
|
||||||
|
{
|
||||||
|
cancellationTokenSource?.Cancel();
|
||||||
|
cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
ClearInternal();
|
||||||
|
|
||||||
|
var banner = user?.TournamentBanner;
|
||||||
|
|
||||||
|
if (banner != null)
|
||||||
|
{
|
||||||
|
Show();
|
||||||
|
|
||||||
|
LoadComponentAsync(new DrawableTournamentBanner(banner), AddInternal, cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
cancellationTokenSource?.Cancel();
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
|
{
|
||||||
|
[LongRunningLoad]
|
||||||
|
public partial class DrawableTournamentBanner : OsuClickableContainer
|
||||||
|
{
|
||||||
|
private readonly TournamentBanner banner;
|
||||||
|
|
||||||
|
public DrawableTournamentBanner(TournamentBanner banner)
|
||||||
|
{
|
||||||
|
this.banner = banner;
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(LargeTextureStore textures, OsuGame? game, IAPIProvider api)
|
||||||
|
{
|
||||||
|
Child = new Sprite
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Texture = textures.Get(banner.Image),
|
||||||
|
};
|
||||||
|
|
||||||
|
Action = () => game?.OpenUrlExternally($@"{api.WebsiteRootUrl}/community/tournaments/{banner.TournamentId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
this.FadeInFromZero(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override LocalisableString TooltipText => "view in browser";
|
||||||
|
}
|
||||||
|
}
|
@ -35,6 +35,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
CornerRadius = 8;
|
CornerRadius = 8;
|
||||||
|
|
||||||
TooltipText = group.Name;
|
TooltipText = group.Name;
|
||||||
|
|
||||||
|
if (group.IsProbationary)
|
||||||
|
{
|
||||||
|
Alpha = 0.6f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -47,7 +52,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = colourProvider?.Background6 ?? Colour4.Black
|
Colour = colourProvider?.Background6 ?? Colour4.Black,
|
||||||
|
// Normal badges background opacity is 75%, probationary is full opacity as the whole badge gets a bit transparent
|
||||||
|
// Goal is to match osu-web so this is the most accurate it can be, its a bit scuffed but it is what it is
|
||||||
|
// Source: https://github.com/ppy/osu-web/blob/master/resources/css/bem/user-group-badge.less#L50
|
||||||
|
Alpha = group.IsProbationary ? 1 : 0.75f,
|
||||||
},
|
},
|
||||||
innerContainer = new FillFlowContainer
|
innerContainer = new FillFlowContainer
|
||||||
{
|
{
|
||||||
|
@ -66,10 +66,12 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
{
|
{
|
||||||
int days = ranked_days - index + 1;
|
int days = ranked_days - index + 1;
|
||||||
|
|
||||||
return new UserGraphTooltipContent(
|
return new UserGraphTooltipContent
|
||||||
UsersStrings.ShowRankGlobalSimple,
|
{
|
||||||
rank.ToLocalisableString("\\##,##0"),
|
Name = UsersStrings.ShowRankGlobalSimple,
|
||||||
days == 0 ? "now" : $"{"day".ToQuantity(days)} ago");
|
Count = rank.ToLocalisableString("\\##,##0"),
|
||||||
|
Time = days == 0 ? "now" : $"{"day".ToQuantity(days)} ago",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.Cursor;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Overlays.Profile.Header.Components;
|
using osu.Game.Overlays.Profile.Header.Components;
|
||||||
@ -104,76 +103,69 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
Colour = Colour4.Black.Opacity(0.25f),
|
Colour = Colour4.Black.Opacity(0.25f),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new OsuContextMenuContainer
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
AutoSizeAxes = Axes.Both,
|
||||||
Origin = Anchor.BottomLeft,
|
Direction = FillDirection.Vertical,
|
||||||
RelativeSizeAxes = Axes.Y,
|
Anchor = Anchor.CentreLeft,
|
||||||
AutoSizeAxes = Axes.X,
|
Origin = Anchor.CentreLeft,
|
||||||
Child = new FillFlowContainer
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
new FillFlowContainer
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(5, 0),
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
usernameText = new OsuSpriteText
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Spacing = new Vector2(5, 0),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
usernameText = new OsuSpriteText
|
Font = OsuFont.GetFont(size: 24, weight: FontWeight.Regular)
|
||||||
{
|
},
|
||||||
Font = OsuFont.GetFont(size: 24, weight: FontWeight.Regular)
|
supporterTag = new SupporterIcon
|
||||||
},
|
|
||||||
supporterTag = new SupporterIcon
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Height = 15,
|
|
||||||
},
|
|
||||||
openUserExternally = new ExternalLinkButton
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
},
|
|
||||||
groupBadgeFlow = new GroupBadgeFlow
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
titleText = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular),
|
|
||||||
Margin = new MarginPadding { Bottom = 5 }
|
|
||||||
},
|
|
||||||
new FillFlowContainer
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
userFlag = new UpdateableFlag
|
Anchor = Anchor.CentreLeft,
|
||||||
{
|
Origin = Anchor.CentreLeft,
|
||||||
Size = new Vector2(28, 20),
|
Height = 15,
|
||||||
ShowPlaceholderOnUnknown = false,
|
},
|
||||||
},
|
openUserExternally = new ExternalLinkButton
|
||||||
userCountryText = new OsuSpriteText
|
{
|
||||||
{
|
Anchor = Anchor.CentreLeft,
|
||||||
Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular),
|
Origin = Anchor.CentreLeft,
|
||||||
Margin = new MarginPadding { Left = 5 },
|
},
|
||||||
Origin = Anchor.CentreLeft,
|
groupBadgeFlow = new GroupBadgeFlow
|
||||||
Anchor = Anchor.CentreLeft,
|
{
|
||||||
}
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
titleText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular),
|
||||||
|
Margin = new MarginPadding { Bottom = 5 }
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
userFlag = new UpdateableFlag
|
||||||
|
{
|
||||||
|
Size = new Vector2(28, 20),
|
||||||
|
ShowPlaceholderOnUnknown = false,
|
||||||
|
},
|
||||||
|
userCountryText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.GetFont(size: 14f, weight: FontWeight.Regular),
|
||||||
|
Margin = new MarginPadding { Left = 5 },
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
},
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -47,6 +47,10 @@ namespace osu.Game.Overlays.Profile
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
User = { BindTarget = User },
|
User = { BindTarget = User },
|
||||||
},
|
},
|
||||||
|
new BannerHeaderContainer
|
||||||
|
{
|
||||||
|
User = { BindTarget = User },
|
||||||
|
},
|
||||||
new BadgeHeaderContainer
|
new BadgeHeaderContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
@ -27,9 +27,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
|
|||||||
protected override float GetDataPointHeight(long playCount) => playCount;
|
protected override float GetDataPointHeight(long playCount) => playCount;
|
||||||
|
|
||||||
protected override UserGraphTooltipContent GetTooltipContent(DateTime date, long playCount) =>
|
protected override UserGraphTooltipContent GetTooltipContent(DateTime date, long playCount) =>
|
||||||
new UserGraphTooltipContent(
|
new UserGraphTooltipContent
|
||||||
tooltipCounterName,
|
{
|
||||||
playCount.ToLocalisableString("N0"),
|
Name = tooltipCounterName,
|
||||||
date.ToLocalisableString("MMMM yyyy"));
|
Count = playCount.ToLocalisableString("N0"),
|
||||||
|
Time = date.ToLocalisableString("MMMM yyyy")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,16 +298,8 @@ namespace osu.Game.Overlays.Profile
|
|||||||
|
|
||||||
public class UserGraphTooltipContent
|
public class UserGraphTooltipContent
|
||||||
{
|
{
|
||||||
// todo: could use init-only properties on C# 9 which read better than a constructor.
|
public LocalisableString Name { get; init; }
|
||||||
public LocalisableString Name { get; }
|
public LocalisableString Count { get; init; }
|
||||||
public LocalisableString Count { get; }
|
public LocalisableString Time { get; init; }
|
||||||
public LocalisableString Time { get; }
|
|
||||||
|
|
||||||
public UserGraphTooltipContent(LocalisableString name, LocalisableString count, LocalisableString time)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Count = count;
|
|
||||||
Time = time;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
|||||||
{
|
{
|
||||||
protected override Drawable CreateControl()
|
protected override Drawable CreateControl()
|
||||||
{
|
{
|
||||||
var sliderBar = (OsuSliderBar<double>)base.CreateControl();
|
var sliderBar = (RoundedSliderBar<double>)base.CreateControl();
|
||||||
sliderBar.PlaySamplesOnAdjust = false;
|
sliderBar.PlaySamplesOnAdjust = false;
|
||||||
return sliderBar;
|
return sliderBar;
|
||||||
}
|
}
|
||||||
|
@ -329,7 +329,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class UIScaleSlider : OsuSliderBar<float>
|
private partial class UIScaleSlider : RoundedSliderBar<float>
|
||||||
{
|
{
|
||||||
public override LocalisableString TooltipText => base.TooltipText + "x";
|
public override LocalisableString TooltipText => base.TooltipText + "x";
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class SensitivitySlider : OsuSliderBar<double>
|
public partial class SensitivitySlider : RoundedSliderBar<double>
|
||||||
{
|
{
|
||||||
public override LocalisableString TooltipText => Current.Disabled ? MouseSettingsStrings.EnableHighPrecisionForSensitivityAdjust : $"{base.TooltipText}x";
|
public override LocalisableString TooltipText => Current.Disabled ? MouseSettingsStrings.EnableHighPrecisionForSensitivityAdjust : $"{base.TooltipText}x";
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A slider intended to show a "size" multiplier number, where 1x is 1.0.
|
/// A slider intended to show a "size" multiplier number, where 1x is 1.0.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class SizeSlider<T> : OsuSliderBar<T>
|
public partial class SizeSlider<T> : RoundedSliderBar<T>
|
||||||
where T : struct, IEquatable<T>, IComparable<T>, IConvertible, IFormattable
|
where T : struct, IEquatable<T>, IComparable<T>, IConvertible, IFormattable
|
||||||
{
|
{
|
||||||
public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x", NumberFormatInfo.CurrentInfo);
|
public override LocalisableString TooltipText => Current.Value.ToString(@"0.##x", NumberFormatInfo.CurrentInfo);
|
||||||
|
@ -10,14 +10,14 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
public partial class SettingsSlider<T> : SettingsSlider<T, OsuSliderBar<T>>
|
public partial class SettingsSlider<T> : SettingsSlider<T, RoundedSliderBar<T>>
|
||||||
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class SettingsSlider<TValue, TSlider> : SettingsItem<TValue>
|
public partial class SettingsSlider<TValue, TSlider> : SettingsItem<TValue>
|
||||||
where TValue : struct, IEquatable<TValue>, IComparable<TValue>, IConvertible
|
where TValue : struct, IEquatable<TValue>, IComparable<TValue>, IConvertible
|
||||||
where TSlider : OsuSliderBar<TValue>, new()
|
where TSlider : RoundedSliderBar<TValue>, new()
|
||||||
{
|
{
|
||||||
protected override Drawable CreateControl() => new TSlider
|
protected override Drawable CreateControl() => new TSlider
|
||||||
{
|
{
|
||||||
|
@ -148,9 +148,9 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
component.Origin = Anchor.Centre;
|
component.Origin = Anchor.Centre;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void UpdateAfterChildren()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
if (component.DrawSize != Vector2.Zero)
|
if (component.DrawSize != Vector2.Zero)
|
||||||
{
|
{
|
||||||
|
@ -24,6 +24,7 @@ using osu.Game.Graphics.Cursor;
|
|||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Overlays.OSD;
|
using osu.Game.Overlays.OSD;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Components;
|
using osu.Game.Screens.Edit.Components;
|
||||||
using osu.Game.Screens.Edit.Components.Menus;
|
using osu.Game.Screens.Edit.Components.Menus;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
@ -31,7 +32,7 @@ using osu.Game.Skinning;
|
|||||||
namespace osu.Game.Overlays.SkinEditor
|
namespace osu.Game.Overlays.SkinEditor
|
||||||
{
|
{
|
||||||
[Cached(typeof(SkinEditor))]
|
[Cached(typeof(SkinEditor))]
|
||||||
public partial class SkinEditor : VisibilityContainer, ICanAcceptFiles, IKeyBindingHandler<PlatformAction>
|
public partial class SkinEditor : VisibilityContainer, ICanAcceptFiles, IKeyBindingHandler<PlatformAction>, IEditorChangeHandler
|
||||||
{
|
{
|
||||||
public const double TRANSITION_DURATION = 300;
|
public const double TRANSITION_DURATION = 300;
|
||||||
|
|
||||||
@ -72,6 +73,11 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
private EditorSidebar componentsSidebar = null!;
|
private EditorSidebar componentsSidebar = null!;
|
||||||
private EditorSidebar settingsSidebar = null!;
|
private EditorSidebar settingsSidebar = null!;
|
||||||
|
|
||||||
|
private SkinEditorChangeHandler? changeHandler;
|
||||||
|
|
||||||
|
private EditorMenuItem undoMenuItem = null!;
|
||||||
|
private EditorMenuItem redoMenuItem = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OnScreenDisplay? onScreenDisplay { get; set; }
|
private OnScreenDisplay? onScreenDisplay { get; set; }
|
||||||
|
|
||||||
@ -131,6 +137,14 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, () => skinEditorOverlay?.Hide()),
|
new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, () => skinEditorOverlay?.Hide()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
new MenuItem(CommonStrings.MenuBarEdit)
|
||||||
|
{
|
||||||
|
Items = new[]
|
||||||
|
{
|
||||||
|
undoMenuItem = new EditorMenuItem(CommonStrings.Undo, MenuItemType.Standard, Undo),
|
||||||
|
redoMenuItem = new EditorMenuItem(CommonStrings.Redo, MenuItemType.Standard, Redo),
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
headerText = new OsuTextFlowContainer
|
headerText = new OsuTextFlowContainer
|
||||||
@ -210,6 +224,14 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
{
|
{
|
||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
|
case PlatformAction.Undo:
|
||||||
|
Undo();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case PlatformAction.Redo:
|
||||||
|
Redo();
|
||||||
|
return true;
|
||||||
|
|
||||||
case PlatformAction.Save:
|
case PlatformAction.Save:
|
||||||
if (e.Repeat)
|
if (e.Repeat)
|
||||||
return false;
|
return false;
|
||||||
@ -229,6 +251,8 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
{
|
{
|
||||||
this.targetScreen = targetScreen;
|
this.targetScreen = targetScreen;
|
||||||
|
|
||||||
|
changeHandler?.Dispose();
|
||||||
|
|
||||||
SelectedComponents.Clear();
|
SelectedComponents.Clear();
|
||||||
|
|
||||||
// Immediately clear the previous blueprint container to ensure it doesn't try to interact with the old target.
|
// Immediately clear the previous blueprint container to ensure it doesn't try to interact with the old target.
|
||||||
@ -241,6 +265,10 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
{
|
{
|
||||||
Debug.Assert(content != null);
|
Debug.Assert(content != null);
|
||||||
|
|
||||||
|
changeHandler = new SkinEditorChangeHandler(targetScreen);
|
||||||
|
changeHandler.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
|
||||||
|
changeHandler.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true);
|
||||||
|
|
||||||
content.Child = new SkinBlueprintContainer(targetScreen);
|
content.Child = new SkinBlueprintContainer(targetScreen);
|
||||||
|
|
||||||
componentsSidebar.Child = new SkinComponentToolbox(getFirstTarget() as CompositeDrawable)
|
componentsSidebar.Child = new SkinComponentToolbox(getFirstTarget() as CompositeDrawable)
|
||||||
@ -333,6 +361,10 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void Undo() => changeHandler?.RestoreState(-1);
|
||||||
|
|
||||||
|
protected void Redo() => changeHandler?.RestoreState(1);
|
||||||
|
|
||||||
public void Save(bool userTriggered = true)
|
public void Save(bool userTriggered = true)
|
||||||
{
|
{
|
||||||
if (!hasBegunMutating)
|
if (!hasBegunMutating)
|
||||||
@ -436,5 +468,27 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Delegation of IEditorChangeHandler
|
||||||
|
|
||||||
|
public event Action? OnStateChange
|
||||||
|
{
|
||||||
|
add => throw new NotImplementedException();
|
||||||
|
remove => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEditorChangeHandler? beginChangeHandler;
|
||||||
|
|
||||||
|
public void BeginChange()
|
||||||
|
{
|
||||||
|
// Change handler may change between begin and end, which can cause unbalanced operations.
|
||||||
|
// Let's track the one that was used when beginning the change so we can call EndChange on it specifically.
|
||||||
|
(beginChangeHandler = changeHandler)?.BeginChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndChange() => beginChangeHandler?.EndChange();
|
||||||
|
public void SaveState() => changeHandler?.SaveState();
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
78
osu.Game/Overlays/SkinEditor/SkinEditorChangeHandler.cs
Normal file
78
osu.Game/Overlays/SkinEditor/SkinEditorChangeHandler.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Extensions;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.SkinEditor
|
||||||
|
{
|
||||||
|
public partial class SkinEditorChangeHandler : EditorChangeHandler
|
||||||
|
{
|
||||||
|
private readonly ISkinnableTarget? firstTarget;
|
||||||
|
|
||||||
|
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
|
||||||
|
private readonly BindableList<ISkinnableDrawable>? components;
|
||||||
|
|
||||||
|
public SkinEditorChangeHandler(Drawable targetScreen)
|
||||||
|
{
|
||||||
|
// To keep things simple, we are currently only handling the current target screen for undo / redo.
|
||||||
|
// In the future we'll want this to cover all changes, even to skin's `InstantiationInfo`.
|
||||||
|
// We'll also need to consider cases where multiple targets are on screen at the same time.
|
||||||
|
|
||||||
|
firstTarget = targetScreen.ChildrenOfType<ISkinnableTarget>().FirstOrDefault();
|
||||||
|
|
||||||
|
if (firstTarget == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
components = new BindableList<ISkinnableDrawable> { BindTarget = firstTarget.Components };
|
||||||
|
components.BindCollectionChanged((_, _) => SaveState());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void WriteCurrentStateToStream(MemoryStream stream)
|
||||||
|
{
|
||||||
|
if (firstTarget == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var skinnableInfos = firstTarget.CreateSkinnableInfo().ToArray();
|
||||||
|
string json = JsonConvert.SerializeObject(skinnableInfos, new JsonSerializerSettings { Formatting = Formatting.Indented });
|
||||||
|
stream.Write(Encoding.UTF8.GetBytes(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplyStateChange(byte[] previousState, byte[] newState)
|
||||||
|
{
|
||||||
|
if (firstTarget == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var deserializedContent = JsonConvert.DeserializeObject<IEnumerable<SkinnableInfo>>(Encoding.UTF8.GetString(newState));
|
||||||
|
|
||||||
|
if (deserializedContent == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SkinnableInfo[] skinnableInfo = deserializedContent.ToArray();
|
||||||
|
Drawable[] targetComponents = firstTarget.Components.OfType<Drawable>().ToArray();
|
||||||
|
|
||||||
|
if (!skinnableInfo.Select(s => s.Type).SequenceEqual(targetComponents.Select(d => d.GetType())))
|
||||||
|
{
|
||||||
|
// Perform a naive full reload for now.
|
||||||
|
firstTarget.Reload(skinnableInfo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
foreach (var drawable in targetComponents)
|
||||||
|
drawable.ApplySkinnableInfo(skinnableInfo[i++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -241,6 +241,8 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
|
|
||||||
private void applyOrigins(Anchor origin)
|
private void applyOrigins(Anchor origin)
|
||||||
{
|
{
|
||||||
|
OnOperationBegan();
|
||||||
|
|
||||||
foreach (var item in SelectedItems)
|
foreach (var item in SelectedItems)
|
||||||
{
|
{
|
||||||
var drawable = (Drawable)item;
|
var drawable = (Drawable)item;
|
||||||
@ -255,6 +257,8 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
|
|
||||||
ApplyClosestAnchor(drawable);
|
ApplyClosestAnchor(drawable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnOperationEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -266,6 +270,8 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
|
|
||||||
private void applyFixedAnchors(Anchor anchor)
|
private void applyFixedAnchors(Anchor anchor)
|
||||||
{
|
{
|
||||||
|
OnOperationBegan();
|
||||||
|
|
||||||
foreach (var item in SelectedItems)
|
foreach (var item in SelectedItems)
|
||||||
{
|
{
|
||||||
var drawable = (Drawable)item;
|
var drawable = (Drawable)item;
|
||||||
@ -273,15 +279,21 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
item.UsesFixedAnchor = true;
|
item.UsesFixedAnchor = true;
|
||||||
applyAnchor(drawable, anchor);
|
applyAnchor(drawable, anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnOperationEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyClosestAnchors()
|
private void applyClosestAnchors()
|
||||||
{
|
{
|
||||||
|
OnOperationBegan();
|
||||||
|
|
||||||
foreach (var item in SelectedItems)
|
foreach (var item in SelectedItems)
|
||||||
{
|
{
|
||||||
item.UsesFixedAnchor = false;
|
item.UsesFixedAnchor = false;
|
||||||
ApplyClosestAnchor((Drawable)item);
|
ApplyClosestAnchor((Drawable)item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnOperationEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Anchor getClosestAnchor(Drawable drawable)
|
private static Anchor getClosestAnchor(Drawable drawable)
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Components;
|
using osu.Game.Screens.Edit.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -13,19 +16,41 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
{
|
{
|
||||||
internal partial class SkinSettingsToolbox : EditorSidebarSection
|
internal partial class SkinSettingsToolbox : EditorSidebarSection
|
||||||
{
|
{
|
||||||
|
[Resolved]
|
||||||
|
private IEditorChangeHandler? changeHandler { get; set; }
|
||||||
|
|
||||||
protected override Container<Drawable> Content { get; }
|
protected override Container<Drawable> Content { get; }
|
||||||
|
|
||||||
|
private readonly Drawable component;
|
||||||
|
|
||||||
public SkinSettingsToolbox(Drawable component)
|
public SkinSettingsToolbox(Drawable component)
|
||||||
: base(SkinEditorStrings.Settings(component.GetType().Name))
|
: base(SkinEditorStrings.Settings(component.GetType().Name))
|
||||||
{
|
{
|
||||||
|
this.component = component;
|
||||||
|
|
||||||
base.Content.Add(Content = new FillFlowContainer
|
base.Content.Add(Content = new FillFlowContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(10),
|
Spacing = new Vector2(10),
|
||||||
Children = component.CreateSettingsControls().ToArray()
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
var controls = component.CreateSettingsControls().ToArray();
|
||||||
|
|
||||||
|
Content.AddRange(controls);
|
||||||
|
|
||||||
|
// track any changes to update undo states.
|
||||||
|
foreach (var c in controls.OfType<ISettingsItem>())
|
||||||
|
{
|
||||||
|
// TODO: SettingChanged is called too often for cases like SettingsTextBox and SettingsSlider.
|
||||||
|
// We will want to expose a SettingCommitted or similar to make this work better.
|
||||||
|
c.SettingChanged += () => changeHandler?.SaveState();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online;
|
using osu.Game.Online;
|
||||||
@ -100,17 +101,22 @@ namespace osu.Game.Overlays
|
|||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
};
|
};
|
||||||
|
|
||||||
Add(sectionsContainer = new ProfileSectionsContainer
|
Add(new OsuContextMenuContainer
|
||||||
{
|
{
|
||||||
ExpandableHeader = Header,
|
RelativeSizeAxes = Axes.Both,
|
||||||
FixedHeader = tabs,
|
Child = sectionsContainer = new ProfileSectionsContainer
|
||||||
HeaderBackground = new Box
|
|
||||||
{
|
{
|
||||||
// this is only visible as the ProfileTabControl background
|
ExpandableHeader = Header,
|
||||||
Colour = ColourProvider.Background5,
|
FixedHeader = tabs,
|
||||||
RelativeSizeAxes = Axes.Both
|
HeaderBackground = new Box
|
||||||
},
|
{
|
||||||
|
// this is only visible as the ProfileTabControl background
|
||||||
|
Colour = ColourProvider.Background5,
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
sectionsContainer.SelectedSection.ValueChanged += section =>
|
sectionsContainer.SelectedSection.ValueChanged += section =>
|
||||||
{
|
{
|
||||||
if (lastSection != section.NewValue)
|
if (lastSection != section.NewValue)
|
||||||
|
@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuSliderBar<float>
|
new RoundedSliderBar<float>
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Current = currentNumber,
|
Current = currentNumber,
|
||||||
|
@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class PercentSlider : OsuSliderBar<double>
|
public partial class PercentSlider : RoundedSliderBar<double>
|
||||||
{
|
{
|
||||||
public PercentSlider()
|
public PercentSlider()
|
||||||
{
|
{
|
||||||
|
@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class MuteComboSlider : OsuSliderBar<int>
|
public partial class MuteComboSlider : RoundedSliderBar<int>
|
||||||
{
|
{
|
||||||
public override LocalisableString TooltipText => Current.Value == 0 ? "always muted" : base.TooltipText;
|
public override LocalisableString TooltipText => Current.Value == 0 ? "always muted" : base.TooltipText;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class HiddenComboSlider : OsuSliderBar<int>
|
public partial class HiddenComboSlider : RoundedSliderBar<int>
|
||||||
{
|
{
|
||||||
public override LocalisableString TooltipText => Current.Value == 0 ? "always hidden" : base.TooltipText;
|
public override LocalisableString TooltipText => Current.Value == 0 ? "always hidden" : base.TooltipText;
|
||||||
}
|
}
|
||||||
|
@ -439,19 +439,20 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
private List<HitSampleInfo> convertSoundType(LegacyHitSoundType type, SampleBankInfo bankInfo)
|
private List<HitSampleInfo> convertSoundType(LegacyHitSoundType type, SampleBankInfo bankInfo)
|
||||||
{
|
{
|
||||||
// Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario
|
var soundTypes = new List<HitSampleInfo>();
|
||||||
if (!string.IsNullOrEmpty(bankInfo.Filename))
|
|
||||||
{
|
|
||||||
return new List<HitSampleInfo> { new FileHitSampleInfo(bankInfo.Filename, bankInfo.Volume) };
|
|
||||||
}
|
|
||||||
|
|
||||||
var soundTypes = new List<HitSampleInfo>
|
if (string.IsNullOrEmpty(bankInfo.Filename))
|
||||||
{
|
{
|
||||||
new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.BankForNormal, bankInfo.Volume, bankInfo.CustomSampleBank,
|
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.BankForNormal, bankInfo.Volume, bankInfo.CustomSampleBank,
|
||||||
// if the sound type doesn't have the Normal flag set, attach it anyway as a layered sample.
|
// if the sound type doesn't have the Normal flag set, attach it anyway as a layered sample.
|
||||||
// None also counts as a normal non-layered sample: https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hitsounds
|
// None also counts as a normal non-layered sample: https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hitsounds
|
||||||
type != LegacyHitSoundType.None && !type.HasFlagFast(LegacyHitSoundType.Normal))
|
type != LegacyHitSoundType.None && !type.HasFlagFast(LegacyHitSoundType.Normal)));
|
||||||
};
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Todo: This should set the normal SampleInfo if the specified sample file isn't found, but that's a pretty edge-case scenario
|
||||||
|
soundTypes.Add(new FileHitSampleInfo(bankInfo.Filename, bankInfo.Volume));
|
||||||
|
}
|
||||||
|
|
||||||
if (type.HasFlagFast(LegacyHitSoundType.Finish))
|
if (type.HasFlagFast(LegacyHitSoundType.Finish))
|
||||||
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank));
|
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank));
|
||||||
|
@ -7,21 +7,28 @@ using System.Diagnostics;
|
|||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Framework.Localisation;
|
|
||||||
using osu.Game.Localisation;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Scoring
|
namespace osu.Game.Rulesets.Scoring
|
||||||
{
|
{
|
||||||
public partial class ScoreProcessor : JudgementProcessor
|
public partial class ScoreProcessor : JudgementProcessor
|
||||||
{
|
{
|
||||||
|
private const double accuracy_cutoff_x = 1;
|
||||||
|
private const double accuracy_cutoff_s = 0.95;
|
||||||
|
private const double accuracy_cutoff_a = 0.9;
|
||||||
|
private const double accuracy_cutoff_b = 0.8;
|
||||||
|
private const double accuracy_cutoff_c = 0.7;
|
||||||
|
private const double accuracy_cutoff_d = 0;
|
||||||
|
|
||||||
private const double max_score = 1000000;
|
private const double max_score = 1000000;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -160,7 +167,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue);
|
Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue);
|
||||||
Accuracy.ValueChanged += accuracy =>
|
Accuracy.ValueChanged += accuracy =>
|
||||||
{
|
{
|
||||||
Rank.Value = rankFrom(accuracy.NewValue);
|
Rank.Value = RankFromAccuracy(accuracy.NewValue);
|
||||||
foreach (var mod in Mods.Value.OfType<IApplicableToScoreProcessor>())
|
foreach (var mod in Mods.Value.OfType<IApplicableToScoreProcessor>())
|
||||||
Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue);
|
Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue);
|
||||||
};
|
};
|
||||||
@ -369,22 +376,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScoreRank rankFrom(double acc)
|
|
||||||
{
|
|
||||||
if (acc == 1)
|
|
||||||
return ScoreRank.X;
|
|
||||||
if (acc >= 0.95)
|
|
||||||
return ScoreRank.S;
|
|
||||||
if (acc >= 0.9)
|
|
||||||
return ScoreRank.A;
|
|
||||||
if (acc >= 0.8)
|
|
||||||
return ScoreRank.B;
|
|
||||||
if (acc >= 0.7)
|
|
||||||
return ScoreRank.C;
|
|
||||||
|
|
||||||
return ScoreRank.D;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets this ScoreProcessor to a default state.
|
/// Resets this ScoreProcessor to a default state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -583,6 +574,62 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
hitEvents.Clear();
|
hitEvents.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Static helper methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given an accuracy (0..1), return the correct <see cref="ScoreRank"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static ScoreRank RankFromAccuracy(double accuracy)
|
||||||
|
{
|
||||||
|
if (accuracy == accuracy_cutoff_x)
|
||||||
|
return ScoreRank.X;
|
||||||
|
if (accuracy >= accuracy_cutoff_s)
|
||||||
|
return ScoreRank.S;
|
||||||
|
if (accuracy >= accuracy_cutoff_a)
|
||||||
|
return ScoreRank.A;
|
||||||
|
if (accuracy >= accuracy_cutoff_b)
|
||||||
|
return ScoreRank.B;
|
||||||
|
if (accuracy >= accuracy_cutoff_c)
|
||||||
|
return ScoreRank.C;
|
||||||
|
|
||||||
|
return ScoreRank.D;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a <see cref="ScoreRank"/>, return the cutoff accuracy (0..1).
|
||||||
|
/// Accuracy must be greater than or equal to the cutoff to qualify for the provided rank.
|
||||||
|
/// </summary>
|
||||||
|
public static double AccuracyCutoffFromRank(ScoreRank rank)
|
||||||
|
{
|
||||||
|
switch (rank)
|
||||||
|
{
|
||||||
|
case ScoreRank.X:
|
||||||
|
case ScoreRank.XH:
|
||||||
|
return accuracy_cutoff_x;
|
||||||
|
|
||||||
|
case ScoreRank.S:
|
||||||
|
case ScoreRank.SH:
|
||||||
|
return accuracy_cutoff_s;
|
||||||
|
|
||||||
|
case ScoreRank.A:
|
||||||
|
return accuracy_cutoff_a;
|
||||||
|
|
||||||
|
case ScoreRank.B:
|
||||||
|
return accuracy_cutoff_b;
|
||||||
|
|
||||||
|
case ScoreRank.C:
|
||||||
|
return accuracy_cutoff_c;
|
||||||
|
|
||||||
|
case ScoreRank.D:
|
||||||
|
return accuracy_cutoff_d;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(rank), rank, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stores the required scoring data that fulfils the minimum requirements for a <see cref="ScoreProcessor"/> to calculate score.
|
/// Stores the required scoring data that fulfils the minimum requirements for a <see cref="ScoreProcessor"/> to calculate score.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
40
osu.Game/Screens/Edit/BeatmapEditorChangeHandler.cs
Normal file
40
osu.Game/Screens/Edit/BeatmapEditorChangeHandler.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
public partial class BeatmapEditorChangeHandler : EditorChangeHandler
|
||||||
|
{
|
||||||
|
private readonly LegacyEditorBeatmapPatcher patcher;
|
||||||
|
private readonly EditorBeatmap editorBeatmap;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="EditorChangeHandler"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="editorBeatmap">The <see cref="EditorBeatmap"/> to track the <see cref="HitObject"/>s of.</param>
|
||||||
|
public BeatmapEditorChangeHandler(EditorBeatmap editorBeatmap)
|
||||||
|
{
|
||||||
|
this.editorBeatmap = editorBeatmap;
|
||||||
|
|
||||||
|
editorBeatmap.TransactionBegan += BeginChange;
|
||||||
|
editorBeatmap.TransactionEnded += EndChange;
|
||||||
|
editorBeatmap.SaveStateTriggered += SaveState;
|
||||||
|
|
||||||
|
patcher = new LegacyEditorBeatmapPatcher(editorBeatmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void WriteCurrentStateToStream(MemoryStream stream)
|
||||||
|
{
|
||||||
|
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
||||||
|
new LegacyBeatmapEncoder(editorBeatmap, editorBeatmap.BeatmapSkin).Encode(sw);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplyStateChange(byte[] previousState, byte[] newState) =>
|
||||||
|
patcher.Patch(previousState, newState);
|
||||||
|
}
|
||||||
|
}
|
@ -157,7 +157,16 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
private bool isNewBeatmap;
|
private bool isNewBeatmap;
|
||||||
|
|
||||||
protected override UserActivity InitialActivity => new UserActivity.Editing(Beatmap.Value.BeatmapInfo);
|
protected override UserActivity InitialActivity
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Beatmap.Value.Metadata.Author.OnlineID == api.LocalUser.Value.OnlineID)
|
||||||
|
return new UserActivity.EditingBeatmap(Beatmap.Value.BeatmapInfo);
|
||||||
|
|
||||||
|
return new UserActivity.ModdingBeatmap(Beatmap.Value.BeatmapInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
@ -240,7 +249,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
if (canSave)
|
if (canSave)
|
||||||
{
|
{
|
||||||
changeHandler = new EditorChangeHandler(editorBeatmap);
|
changeHandler = new BeatmapEditorChangeHandler(editorBeatmap);
|
||||||
dependencies.CacheAs<IEditorChangeHandler>(changeHandler);
|
dependencies.CacheAs<IEditorChangeHandler>(changeHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,31 +1,25 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Game.Beatmaps.Formats;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit
|
namespace osu.Game.Screens.Edit
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tracks changes to the <see cref="Editor"/>.
|
/// Tracks changes to the <see cref="Editor"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class EditorChangeHandler : TransactionalCommitComponent, IEditorChangeHandler
|
public abstract partial class EditorChangeHandler : TransactionalCommitComponent, IEditorChangeHandler
|
||||||
{
|
{
|
||||||
public readonly Bindable<bool> CanUndo = new Bindable<bool>();
|
public readonly Bindable<bool> CanUndo = new Bindable<bool>();
|
||||||
public readonly Bindable<bool> CanRedo = new Bindable<bool>();
|
public readonly Bindable<bool> CanRedo = new Bindable<bool>();
|
||||||
|
|
||||||
public event Action OnStateChange;
|
public event Action? OnStateChange;
|
||||||
|
|
||||||
private readonly LegacyEditorBeatmapPatcher patcher;
|
|
||||||
private readonly List<byte[]> savedStates = new List<byte[]>();
|
private readonly List<byte[]> savedStates = new List<byte[]>();
|
||||||
|
|
||||||
private int currentState = -1;
|
private int currentState = -1;
|
||||||
@ -37,32 +31,28 @@ namespace osu.Game.Screens.Edit
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
ensureStateSaved();
|
||||||
|
|
||||||
using (var stream = new MemoryStream(savedStates[currentState]))
|
using (var stream = new MemoryStream(savedStates[currentState]))
|
||||||
return stream.ComputeSHA2Hash();
|
return stream.ComputeSHA2Hash();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly EditorBeatmap editorBeatmap;
|
|
||||||
private bool isRestoring;
|
private bool isRestoring;
|
||||||
|
|
||||||
public const int MAX_SAVED_STATES = 50;
|
public const int MAX_SAVED_STATES = 50;
|
||||||
|
|
||||||
/// <summary>
|
public override void BeginChange()
|
||||||
/// Creates a new <see cref="EditorChangeHandler"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="editorBeatmap">The <see cref="EditorBeatmap"/> to track the <see cref="HitObject"/>s of.</param>
|
|
||||||
public EditorChangeHandler(EditorBeatmap editorBeatmap)
|
|
||||||
{
|
{
|
||||||
this.editorBeatmap = editorBeatmap;
|
ensureStateSaved();
|
||||||
|
|
||||||
editorBeatmap.TransactionBegan += BeginChange;
|
base.BeginChange();
|
||||||
editorBeatmap.TransactionEnded += EndChange;
|
}
|
||||||
editorBeatmap.SaveStateTriggered += SaveState;
|
|
||||||
|
|
||||||
patcher = new LegacyEditorBeatmapPatcher(editorBeatmap);
|
private void ensureStateSaved()
|
||||||
|
{
|
||||||
// Initial state.
|
if (savedStates.Count == 0)
|
||||||
SaveState();
|
SaveState();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateState()
|
protected override void UpdateState()
|
||||||
@ -72,9 +62,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
using (var stream = new MemoryStream())
|
using (var stream = new MemoryStream())
|
||||||
{
|
{
|
||||||
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
WriteCurrentStateToStream(stream);
|
||||||
new LegacyBeatmapEncoder(editorBeatmap, editorBeatmap.BeatmapSkin).Encode(sw);
|
|
||||||
|
|
||||||
byte[] newState = stream.ToArray();
|
byte[] newState = stream.ToArray();
|
||||||
|
|
||||||
// if the previous state is binary equal we don't need to push a new one, unless this is the initial state.
|
// if the previous state is binary equal we don't need to push a new one, unless this is the initial state.
|
||||||
@ -113,7 +101,8 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
isRestoring = true;
|
isRestoring = true;
|
||||||
|
|
||||||
patcher.Patch(savedStates[currentState], savedStates[newState]);
|
ApplyStateChange(savedStates[currentState], savedStates[newState]);
|
||||||
|
|
||||||
currentState = newState;
|
currentState = newState;
|
||||||
|
|
||||||
isRestoring = false;
|
isRestoring = false;
|
||||||
@ -122,6 +111,20 @@ namespace osu.Game.Screens.Edit
|
|||||||
updateBindables();
|
updateBindables();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a serialised copy of the currently tracked state to the provided stream.
|
||||||
|
/// This will be stored as a state which can be restored in the future.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream which the state should be written to.</param>
|
||||||
|
protected abstract void WriteCurrentStateToStream(MemoryStream stream);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a previous and new state, apply any changes required to bring the current state in line with the new state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="previousState">The previous (current before this call) serialised state.</param>
|
||||||
|
/// <param name="newState">The new state to be applied.</param>
|
||||||
|
protected abstract void ApplyStateChange(byte[] previousState, byte[] newState);
|
||||||
|
|
||||||
private void updateBindables()
|
private void updateBindables()
|
||||||
{
|
{
|
||||||
CanUndo.Value = savedStates.Count > 0 && currentState > 0;
|
CanUndo.Value = savedStates.Count > 0 && currentState > 0;
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Screens;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.GameplayTest
|
namespace osu.Game.Screens.Edit.GameplayTest
|
||||||
{
|
{
|
||||||
@ -15,6 +16,8 @@ namespace osu.Game.Screens.Edit.GameplayTest
|
|||||||
private readonly Editor editor;
|
private readonly Editor editor;
|
||||||
private readonly EditorState editorState;
|
private readonly EditorState editorState;
|
||||||
|
|
||||||
|
protected override UserActivity InitialActivity => new UserActivity.TestingBeatmap(Beatmap.Value.BeatmapInfo, Ruleset.Value);
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private MusicController musicController { get; set; } = null!;
|
private MusicController musicController { get; set; } = null!;
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit
|
namespace osu.Game.Screens.Edit
|
||||||
@ -11,12 +10,13 @@ namespace osu.Game.Screens.Edit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interface for a component that manages changes in the <see cref="Editor"/>.
|
/// Interface for a component that manages changes in the <see cref="Editor"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Cached]
|
||||||
public interface IEditorChangeHandler
|
public interface IEditorChangeHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired whenever a state change occurs.
|
/// Fired whenever a state change occurs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event Action OnStateChange;
|
event Action? OnStateChange;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Begins a bulk state change event. <see cref="EndChange"/> should be invoked soon after.
|
/// Begins a bulk state change event. <see cref="EndChange"/> should be invoked soon after.
|
||||||
|
209
osu.Game/Screens/Edit/Timing/ControlPointList.cs
Normal file
209
osu.Game/Screens/Edit/Timing/ControlPointList.cs
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Timing
|
||||||
|
{
|
||||||
|
public partial class ControlPointList : CompositeDrawable
|
||||||
|
{
|
||||||
|
private OsuButton deleteButton = null!;
|
||||||
|
private ControlPointTable table = null!;
|
||||||
|
private OsuScrollContainer scroll = null!;
|
||||||
|
private RoundedButton addButton = null!;
|
||||||
|
|
||||||
|
private readonly IBindableList<ControlPointGroup> controlPointGroups = new BindableList<ControlPointGroup>();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private EditorClock clock { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
protected EditorBeatmap Beatmap { get; private set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private Bindable<ControlPointGroup?> selectedGroup { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IEditorChangeHandler? changeHandler { get; set; }
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colours)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
const float margins = 10;
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colours.Background4,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colours.Background3,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = ControlPointTable.TIMING_COLUMN_WIDTH + margins,
|
||||||
|
},
|
||||||
|
scroll = new OsuScrollContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = table = new ControlPointTable(),
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Margin = new MarginPadding(margins),
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
deleteButton = new RoundedButton
|
||||||
|
{
|
||||||
|
Text = "-",
|
||||||
|
Size = new Vector2(30, 30),
|
||||||
|
Action = delete,
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
},
|
||||||
|
addButton = new RoundedButton
|
||||||
|
{
|
||||||
|
Action = addNew,
|
||||||
|
Size = new Vector2(160, 30),
|
||||||
|
Anchor = Anchor.BottomRight,
|
||||||
|
Origin = Anchor.BottomRight,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
selectedGroup.BindValueChanged(selected =>
|
||||||
|
{
|
||||||
|
deleteButton.Enabled.Value = selected.NewValue != null;
|
||||||
|
|
||||||
|
addButton.Text = selected.NewValue != null
|
||||||
|
? "+ Clone to current time"
|
||||||
|
: "+ Add at current time";
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
controlPointGroups.BindTo(Beatmap.ControlPointInfo.Groups);
|
||||||
|
controlPointGroups.BindCollectionChanged((_, _) =>
|
||||||
|
{
|
||||||
|
table.ControlGroups = controlPointGroups;
|
||||||
|
changeHandler?.SaveState();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
table.OnRowSelected += drawable => scroll.ScrollIntoView(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e)
|
||||||
|
{
|
||||||
|
selectedGroup.Value = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
trackActivePoint();
|
||||||
|
|
||||||
|
addButton.Enabled.Value = clock.CurrentTimeAccurate != selectedGroup.Value?.Time;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type? trackedType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given the user has selected a control point group, we want to track any group which is
|
||||||
|
/// active at the current point in time which matches the type the user has selected.
|
||||||
|
///
|
||||||
|
/// So if the user is currently looking at a timing point and seeks into the future, a
|
||||||
|
/// future timing point would be automatically selected if it is now the new "current" point.
|
||||||
|
/// </summary>
|
||||||
|
private void trackActivePoint()
|
||||||
|
{
|
||||||
|
// For simplicity only match on the first type of the active control point.
|
||||||
|
if (selectedGroup.Value == null)
|
||||||
|
trackedType = null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the selected group only has one control point, update the tracking type.
|
||||||
|
if (selectedGroup.Value.ControlPoints.Count == 1)
|
||||||
|
trackedType = selectedGroup.Value?.ControlPoints.Single().GetType();
|
||||||
|
// If the selected group has more than one control point, choose the first as the tracking type
|
||||||
|
// if we don't already have a singular tracked type.
|
||||||
|
else if (trackedType == null)
|
||||||
|
trackedType = selectedGroup.Value?.ControlPoints.FirstOrDefault()?.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedType != null)
|
||||||
|
{
|
||||||
|
// We don't have an efficient way of looking up groups currently, only individual point types.
|
||||||
|
// To improve the efficiency of this in the future, we should reconsider the overall structure of ControlPointInfo.
|
||||||
|
|
||||||
|
// Find the next group which has the same type as the selected one.
|
||||||
|
var found = Beatmap.ControlPointInfo.Groups
|
||||||
|
.Where(g => g.ControlPoints.Any(cp => cp.GetType() == trackedType))
|
||||||
|
.LastOrDefault(g => g.Time <= clock.CurrentTimeAccurate);
|
||||||
|
|
||||||
|
if (found != null)
|
||||||
|
selectedGroup.Value = found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void delete()
|
||||||
|
{
|
||||||
|
if (selectedGroup.Value == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Beatmap.ControlPointInfo.RemoveGroup(selectedGroup.Value);
|
||||||
|
|
||||||
|
selectedGroup.Value = Beatmap.ControlPointInfo.Groups.FirstOrDefault(g => g.Time >= clock.CurrentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addNew()
|
||||||
|
{
|
||||||
|
bool isFirstControlPoint = !Beatmap.ControlPointInfo.TimingPoints.Any();
|
||||||
|
|
||||||
|
var group = Beatmap.ControlPointInfo.GroupAt(clock.CurrentTime, true);
|
||||||
|
|
||||||
|
if (isFirstControlPoint)
|
||||||
|
group.Add(new TimingControlPoint());
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Try and create matching types from the currently selected control point.
|
||||||
|
var selected = selectedGroup.Value;
|
||||||
|
|
||||||
|
if (selected != null && !ReferenceEquals(selected, group))
|
||||||
|
{
|
||||||
|
foreach (var controlPoint in selected.ControlPoints)
|
||||||
|
{
|
||||||
|
group.Add(controlPoint.DeepClone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedGroup.Value = group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,11 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
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.Input.Events;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
|
||||||
using osu.Game.Overlays;
|
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Timing
|
namespace osu.Game.Screens.Edit.Timing
|
||||||
{
|
{
|
||||||
@ -23,6 +14,9 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
[Cached]
|
[Cached]
|
||||||
public readonly Bindable<ControlPointGroup> SelectedGroup = new Bindable<ControlPointGroup>();
|
public readonly Bindable<ControlPointGroup> SelectedGroup = new Bindable<ControlPointGroup>();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private EditorClock? editorClock { get; set; }
|
||||||
|
|
||||||
public TimingScreen()
|
public TimingScreen()
|
||||||
: base(EditorScreenMode.Timing)
|
: base(EditorScreenMode.Timing)
|
||||||
{
|
{
|
||||||
@ -46,192 +40,17 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public partial class ControlPointList : CompositeDrawable
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
private OsuButton deleteButton = null!;
|
base.LoadComplete();
|
||||||
private ControlPointTable table = null!;
|
|
||||||
private OsuScrollContainer scroll = null!;
|
|
||||||
private RoundedButton addButton = null!;
|
|
||||||
|
|
||||||
private readonly IBindableList<ControlPointGroup> controlPointGroups = new BindableList<ControlPointGroup>();
|
if (editorClock != null)
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private EditorClock clock { get; set; } = null!;
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
protected EditorBeatmap Beatmap { get; private set; } = null!;
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private Bindable<ControlPointGroup?> selectedGroup { get; set; } = null!;
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private IEditorChangeHandler? changeHandler { get; set; }
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OverlayColourProvider colours)
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
// When entering the timing screen, let's choose the closest valid timing point.
|
||||||
|
// This will emulate the osu-stable behaviour where a metronome and timing information
|
||||||
const float margins = 10;
|
// are presented on entering the screen.
|
||||||
InternalChildren = new Drawable[]
|
var nearestTimingPoint = EditorBeatmap.ControlPointInfo.TimingPointAt(editorClock.CurrentTime);
|
||||||
{
|
SelectedGroup.Value = EditorBeatmap.ControlPointInfo.GroupAt(nearestTimingPoint.Time);
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Colour = colours.Background4,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Colour = colours.Background3,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Width = ControlPointTable.TIMING_COLUMN_WIDTH + margins,
|
|
||||||
},
|
|
||||||
scroll = new OsuScrollContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Child = table = new ControlPointTable(),
|
|
||||||
},
|
|
||||||
new FillFlowContainer
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.BottomRight,
|
|
||||||
Origin = Anchor.BottomRight,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Margin = new MarginPadding(margins),
|
|
||||||
Spacing = new Vector2(5),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
deleteButton = new RoundedButton
|
|
||||||
{
|
|
||||||
Text = "-",
|
|
||||||
Size = new Vector2(30, 30),
|
|
||||||
Action = delete,
|
|
||||||
Anchor = Anchor.BottomRight,
|
|
||||||
Origin = Anchor.BottomRight,
|
|
||||||
},
|
|
||||||
addButton = new RoundedButton
|
|
||||||
{
|
|
||||||
Action = addNew,
|
|
||||||
Size = new Vector2(160, 30),
|
|
||||||
Anchor = Anchor.BottomRight,
|
|
||||||
Origin = Anchor.BottomRight,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
selectedGroup.BindValueChanged(selected =>
|
|
||||||
{
|
|
||||||
deleteButton.Enabled.Value = selected.NewValue != null;
|
|
||||||
|
|
||||||
addButton.Text = selected.NewValue != null
|
|
||||||
? "+ Clone to current time"
|
|
||||||
: "+ Add at current time";
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
controlPointGroups.BindTo(Beatmap.ControlPointInfo.Groups);
|
|
||||||
controlPointGroups.BindCollectionChanged((_, _) =>
|
|
||||||
{
|
|
||||||
table.ControlGroups = controlPointGroups;
|
|
||||||
changeHandler?.SaveState();
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
table.OnRowSelected += drawable => scroll.ScrollIntoView(drawable);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
|
||||||
{
|
|
||||||
selectedGroup.Value = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
trackActivePoint();
|
|
||||||
|
|
||||||
addButton.Enabled.Value = clock.CurrentTimeAccurate != selectedGroup.Value?.Time;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Type? trackedType;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Given the user has selected a control point group, we want to track any group which is
|
|
||||||
/// active at the current point in time which matches the type the user has selected.
|
|
||||||
///
|
|
||||||
/// So if the user is currently looking at a timing point and seeks into the future, a
|
|
||||||
/// future timing point would be automatically selected if it is now the new "current" point.
|
|
||||||
/// </summary>
|
|
||||||
private void trackActivePoint()
|
|
||||||
{
|
|
||||||
// For simplicity only match on the first type of the active control point.
|
|
||||||
if (selectedGroup.Value == null)
|
|
||||||
trackedType = null;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If the selected group only has one control point, update the tracking type.
|
|
||||||
if (selectedGroup.Value.ControlPoints.Count == 1)
|
|
||||||
trackedType = selectedGroup.Value?.ControlPoints.Single().GetType();
|
|
||||||
// If the selected group has more than one control point, choose the first as the tracking type
|
|
||||||
// if we don't already have a singular tracked type.
|
|
||||||
else if (trackedType == null)
|
|
||||||
trackedType = selectedGroup.Value?.ControlPoints.FirstOrDefault()?.GetType();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedType != null)
|
|
||||||
{
|
|
||||||
// We don't have an efficient way of looking up groups currently, only individual point types.
|
|
||||||
// To improve the efficiency of this in the future, we should reconsider the overall structure of ControlPointInfo.
|
|
||||||
|
|
||||||
// Find the next group which has the same type as the selected one.
|
|
||||||
var found = Beatmap.ControlPointInfo.Groups
|
|
||||||
.Where(g => g.ControlPoints.Any(cp => cp.GetType() == trackedType))
|
|
||||||
.LastOrDefault(g => g.Time <= clock.CurrentTimeAccurate);
|
|
||||||
|
|
||||||
if (found != null)
|
|
||||||
selectedGroup.Value = found;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void delete()
|
|
||||||
{
|
|
||||||
if (selectedGroup.Value == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Beatmap.ControlPointInfo.RemoveGroup(selectedGroup.Value);
|
|
||||||
|
|
||||||
selectedGroup.Value = Beatmap.ControlPointInfo.Groups.FirstOrDefault(g => g.Time >= clock.CurrentTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addNew()
|
|
||||||
{
|
|
||||||
bool isFirstControlPoint = !Beatmap.ControlPointInfo.TimingPoints.Any();
|
|
||||||
|
|
||||||
var group = Beatmap.ControlPointInfo.GroupAt(clock.CurrentTime, true);
|
|
||||||
|
|
||||||
if (isFirstControlPoint)
|
|
||||||
group.Add(new TimingControlPoint());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Try and create matching types from the currently selected control point.
|
|
||||||
var selected = selectedGroup.Value;
|
|
||||||
|
|
||||||
if (selected != null && !ReferenceEquals(selected, group))
|
|
||||||
{
|
|
||||||
foreach (var controlPoint in selected.ControlPoints)
|
|
||||||
{
|
|
||||||
group.Add(controlPoint.DeepClone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedGroup.Value = group;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
|
||||||
@ -16,17 +14,17 @@ namespace osu.Game.Screens.Edit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fires whenever a transaction begins. Will not fire on nested transactions.
|
/// Fires whenever a transaction begins. Will not fire on nested transactions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action TransactionBegan;
|
public event Action? TransactionBegan;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fires when the last transaction completes.
|
/// Fires when the last transaction completes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action TransactionEnded;
|
public event Action? TransactionEnded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fires when <see cref="SaveState"/> is called and results in a non-transactional state save.
|
/// Fires when <see cref="SaveState"/> is called and results in a non-transactional state save.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action SaveStateTriggered;
|
public event Action? SaveStateTriggered;
|
||||||
|
|
||||||
public bool TransactionActive => bulkChangesStarted > 0;
|
public bool TransactionActive => bulkChangesStarted > 0;
|
||||||
|
|
||||||
@ -35,7 +33,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signal the beginning of a change.
|
/// Signal the beginning of a change.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void BeginChange()
|
public virtual void BeginChange()
|
||||||
{
|
{
|
||||||
if (bulkChangesStarted++ == 0)
|
if (bulkChangesStarted++ == 0)
|
||||||
TransactionBegan?.Invoke();
|
TransactionBegan?.Invoke();
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -22,29 +20,21 @@ namespace osu.Game.Screens.Play.Break
|
|||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Box
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopLeft,
|
Anchor = Anchor.TopLeft,
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = height,
|
Height = height,
|
||||||
Child = new Box
|
Colour = ColourInfo.GradientVertical(Color4.Black, transparent_black),
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = ColourInfo.GradientVertical(Color4.Black, transparent_black),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
new Container
|
new Box
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = height,
|
Height = height,
|
||||||
Child = new Box
|
Colour = ColourInfo.GradientVertical(transparent_black, Color4.Black),
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = ColourInfo.GradientVertical(transparent_black, Color4.Black),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -69,8 +69,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
{
|
{
|
||||||
var bindable = (IBindable)property.GetValue(component)!;
|
var bindable = (IBindable)property.GetValue(component)!;
|
||||||
|
|
||||||
if (!bindable.IsDefault)
|
Settings.Add(property.Name.ToSnakeCase(), bindable.GetUnderlyingSettingValue());
|
||||||
Settings.Add(property.Name.ToSnakeCase(), bindable.GetUnderlyingSettingValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component is Container<Drawable> container)
|
if (component is Container<Drawable> container)
|
||||||
|
@ -121,8 +121,8 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Child = new UprightAspectMaintainingContainer
|
Child = new UprightAspectMaintainingContainer
|
||||||
{
|
{
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.Centre,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Scaling = ScaleMode.Vertical,
|
Scaling = ScaleMode.Vertical,
|
||||||
ScalingFactor = 0.5f,
|
ScalingFactor = 0.5f,
|
||||||
|
@ -44,6 +44,14 @@ namespace osu.Game.Screens.Play
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void StopAllSamples()
|
||||||
|
{
|
||||||
|
if (!IsLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pauseLoop.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
{
|
{
|
||||||
base.PopIn();
|
base.PopIn();
|
||||||
|
@ -1073,7 +1073,10 @@ namespace osu.Game.Screens.Play
|
|||||||
public override bool OnExiting(ScreenExitEvent e)
|
public override bool OnExiting(ScreenExitEvent e)
|
||||||
{
|
{
|
||||||
screenSuspension?.RemoveAndDisposeImmediately();
|
screenSuspension?.RemoveAndDisposeImmediately();
|
||||||
|
|
||||||
|
// Eagerly clean these up as disposal of child components is asynchronous and may leave sounds playing beyond user expectations.
|
||||||
failAnimationLayer?.Stop();
|
failAnimationLayer?.Stop();
|
||||||
|
PauseOverlay?.StopAllSamples();
|
||||||
|
|
||||||
if (LoadedBeatmapSuccessfully)
|
if (LoadedBeatmapSuccessfully)
|
||||||
{
|
{
|
||||||
|
@ -15,11 +15,11 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
|||||||
public partial class PlayerSliderBar<T> : SettingsSlider<T>
|
public partial class PlayerSliderBar<T> : SettingsSlider<T>
|
||||||
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
||||||
{
|
{
|
||||||
public OsuSliderBar<T> Bar => (OsuSliderBar<T>)Control;
|
public RoundedSliderBar<T> Bar => (RoundedSliderBar<T>)Control;
|
||||||
|
|
||||||
protected override Drawable CreateControl() => new SliderBar();
|
protected override Drawable CreateControl() => new SliderBar();
|
||||||
|
|
||||||
protected partial class SliderBar : OsuSliderBar<T>
|
protected partial class SliderBar : RoundedSliderBar<T>
|
||||||
{
|
{
|
||||||
public SliderBar()
|
public SliderBar()
|
||||||
{
|
{
|
||||||
|
@ -15,6 +15,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Play.HUD;
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
@ -24,6 +25,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private readonly bool replayIsFailedScore;
|
private readonly bool replayIsFailedScore;
|
||||||
|
|
||||||
|
protected override UserActivity InitialActivity => new UserActivity.WatchingReplay(Score.ScoreInfo);
|
||||||
|
|
||||||
// Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108)
|
// Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108)
|
||||||
protected override bool CheckModsAllowFailure()
|
protected override bool CheckModsAllowFailure()
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Online.Spectator;
|
using osu.Game.Online.Spectator;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
@ -14,6 +15,8 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
private readonly Score score;
|
private readonly Score score;
|
||||||
|
|
||||||
|
protected override UserActivity InitialActivity => new UserActivity.SpectatingUser(Score.ScoreInfo);
|
||||||
|
|
||||||
public SoloSpectatorPlayer(Score score, PlayerConfiguration configuration = null)
|
public SoloSpectatorPlayer(Score score, PlayerConfiguration configuration = null)
|
||||||
: base(score, configuration)
|
: base(score, configuration)
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user