mirror of
https://github.com/osukey/osukey.git
synced 2025-05-29 01:17:35 +09:00
Merge branch 'master' into sample-positional-playback-screen-space
This commit is contained in:
commit
d43c66a3dd
110
.github/workflows/ci.yml
vendored
110
.github/workflows/ci.yml
vendored
@ -2,6 +2,60 @@ on: [push, pull_request]
|
||||
name: Continuous Integration
|
||||
|
||||
jobs:
|
||||
inspect-code:
|
||||
name: Code Quality
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side.
|
||||
# https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e
|
||||
- name: Install .NET 3.1.x LTS
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: "3.1.x"
|
||||
|
||||
- name: Install .NET 6.0.x
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: "6.0.x"
|
||||
|
||||
- name: Restore Tools
|
||||
run: dotnet tool restore
|
||||
|
||||
- name: Restore Packages
|
||||
run: dotnet restore
|
||||
|
||||
- name: Restore inspectcode cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/inspectcode
|
||||
key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }}
|
||||
|
||||
- name: CodeFileSanity
|
||||
run: |
|
||||
# TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround.
|
||||
# FIXME: Suppress warnings from templates project
|
||||
exit_code=0
|
||||
while read -r line; do
|
||||
if [[ ! -z "$line" ]]; then
|
||||
echo "::error::$line"
|
||||
exit_code=1
|
||||
fi
|
||||
done <<< $(dotnet codefilesanity)
|
||||
exit $exit_code
|
||||
|
||||
# Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded.
|
||||
# - name: .NET Format (Dry Run)
|
||||
# run: dotnet format --dry-run --check
|
||||
|
||||
- name: InspectCode
|
||||
run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN
|
||||
|
||||
- name: NVika
|
||||
run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ${{matrix.os.fullname}}
|
||||
@ -93,58 +147,4 @@ jobs:
|
||||
# cannot accept .sln(f) files as arguments.
|
||||
# Build just the main game for now.
|
||||
- name: Build
|
||||
run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug
|
||||
|
||||
inspect-code:
|
||||
name: Code Quality
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side.
|
||||
# https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e
|
||||
- name: Install .NET 3.1.x LTS
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: "3.1.x"
|
||||
|
||||
- name: Install .NET 6.0.x
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: "6.0.x"
|
||||
|
||||
- name: Restore Tools
|
||||
run: dotnet tool restore
|
||||
|
||||
- name: Restore Packages
|
||||
run: dotnet restore
|
||||
|
||||
- name: Restore inspectcode cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ github.workspace }}/inspectcode
|
||||
key: inspectcode-${{ hashFiles('.config/dotnet-tools.json') }}-${{ hashFiles('.github/workflows/ci.yml' ) }}
|
||||
|
||||
- name: CodeFileSanity
|
||||
run: |
|
||||
# TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround.
|
||||
# FIXME: Suppress warnings from templates project
|
||||
exit_code=0
|
||||
while read -r line; do
|
||||
if [[ ! -z "$line" ]]; then
|
||||
echo "::error::$line"
|
||||
exit_code=1
|
||||
fi
|
||||
done <<< $(dotnet codefilesanity)
|
||||
exit $exit_code
|
||||
|
||||
# Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded.
|
||||
# - name: .NET Format (Dry Run)
|
||||
# run: dotnet format --dry-run --check
|
||||
|
||||
- name: InspectCode
|
||||
run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN
|
||||
|
||||
- name: NVika
|
||||
run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors
|
||||
run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug
|
@ -52,7 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.417.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.415.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.419.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Desktop.Security
|
||||
public class ElevatedPrivilegesChecker : Component
|
||||
{
|
||||
[Resolved]
|
||||
private NotificationOverlay notifications { get; set; }
|
||||
private INotificationOverlay notifications { get; set; }
|
||||
|
||||
private bool elevated;
|
||||
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Desktop.Updater
|
||||
public class SquirrelUpdateManager : osu.Game.Updater.UpdateManager
|
||||
{
|
||||
private UpdateManager updateManager;
|
||||
private NotificationOverlay notificationOverlay;
|
||||
private INotificationOverlay notificationOverlay;
|
||||
|
||||
public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited();
|
||||
|
||||
@ -39,9 +39,9 @@ namespace osu.Desktop.Updater
|
||||
private readonly SquirrelLogger squirrelLogger = new SquirrelLogger();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(NotificationOverlay notification)
|
||||
private void load(INotificationOverlay notifications)
|
||||
{
|
||||
notificationOverlay = notification;
|
||||
notificationOverlay = notifications;
|
||||
|
||||
SquirrelLocator.CurrentMutable.Register(() => squirrelLogger, typeof(ILogger));
|
||||
}
|
||||
|
108
osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs
Normal file
108
osu.Game.Rulesets.Osu.Tests/LegacyMainCirclePieceTest.cs
Normal file
@ -0,0 +1,108 @@
|
||||
// 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.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class LegacyMainCirclePieceTest : OsuTestScene
|
||||
{
|
||||
private static readonly object?[][] texture_priority_cases =
|
||||
{
|
||||
// default priority lookup
|
||||
new object?[]
|
||||
{
|
||||
// available textures
|
||||
new[] { @"hitcircle", @"hitcircleoverlay" },
|
||||
// priority lookup prefix
|
||||
null,
|
||||
// expected circle and overlay
|
||||
@"hitcircle", @"hitcircleoverlay",
|
||||
},
|
||||
// custom priority lookup
|
||||
new object?[]
|
||||
{
|
||||
new[] { @"hitcircle", @"hitcircleoverlay", @"sliderstartcircle", @"sliderstartcircleoverlay" },
|
||||
@"sliderstartcircle",
|
||||
@"sliderstartcircle", @"sliderstartcircleoverlay",
|
||||
},
|
||||
// when no sprites are available for the specified prefix, fall back to "hitcircle"/"hitcircleoverlay".
|
||||
new object?[]
|
||||
{
|
||||
new[] { @"hitcircle", @"hitcircleoverlay" },
|
||||
@"sliderstartcircle",
|
||||
@"hitcircle", @"hitcircleoverlay",
|
||||
},
|
||||
// when a circle is available for the specified prefix but no overlay exists, no overlay is displayed.
|
||||
new object?[]
|
||||
{
|
||||
new[] { @"hitcircle", @"hitcircleoverlay", @"sliderstartcircle" },
|
||||
@"sliderstartcircle",
|
||||
@"sliderstartcircle", null
|
||||
},
|
||||
// when no circle is available for the specified prefix but an overlay exists, the overlay is ignored.
|
||||
new object?[]
|
||||
{
|
||||
new[] { @"hitcircle", @"hitcircleoverlay", @"sliderstartcircleoverlay" },
|
||||
@"sliderstartcircle",
|
||||
@"hitcircle", @"hitcircleoverlay",
|
||||
}
|
||||
};
|
||||
|
||||
[TestCaseSource(nameof(texture_priority_cases))]
|
||||
public void TestTexturePriorities(string[] textureFilenames, string priorityLookup, string? expectedCircle, string? expectedOverlay)
|
||||
{
|
||||
TestLegacyMainCirclePiece piece = null!;
|
||||
|
||||
AddStep("load circle piece", () =>
|
||||
{
|
||||
var skin = new Mock<ISkinSource>();
|
||||
|
||||
// shouldn't be required as GetTexture(string) calls GetTexture(string, WrapMode, WrapMode) by default,
|
||||
// but moq doesn't handle that well, therefore explicitly requiring to use `CallBase`:
|
||||
// https://github.com/moq/moq4/issues/972
|
||||
skin.Setup(s => s.GetTexture(It.IsAny<string>())).CallBase();
|
||||
|
||||
skin.Setup(s => s.GetTexture(It.IsIn(textureFilenames), It.IsAny<WrapMode>(), It.IsAny<WrapMode>()))
|
||||
.Returns((string componentName, WrapMode _, WrapMode __) => new Texture(1, 1) { AssetName = componentName });
|
||||
|
||||
Child = new DependencyProvidingContainer
|
||||
{
|
||||
CachedDependencies = new (Type, object)[] { (typeof(ISkinSource), skin.Object) },
|
||||
Child = piece = new TestLegacyMainCirclePiece(priorityLookup),
|
||||
};
|
||||
|
||||
var sprites = this.ChildrenOfType<Sprite>().Where(s => s.Texture.AssetName != null).DistinctBy(s => s.Texture.AssetName).ToArray();
|
||||
Debug.Assert(sprites.Length <= 2);
|
||||
});
|
||||
|
||||
AddAssert("check circle sprite", () => piece.CircleSprite?.Texture?.AssetName == expectedCircle);
|
||||
AddAssert("check overlay sprite", () => piece.OverlaySprite?.Texture?.AssetName == expectedOverlay);
|
||||
}
|
||||
|
||||
private class TestLegacyMainCirclePiece : LegacyMainCirclePiece
|
||||
{
|
||||
public new Sprite? CircleSprite => base.CircleSprite.ChildrenOfType<Sprite>().DistinctBy(s => s.Texture.AssetName).SingleOrDefault();
|
||||
public new Sprite? OverlaySprite => base.OverlaySprite.ChildrenOfType<Sprite>().DistinctBy(s => s.Texture.AssetName).SingleOrDefault();
|
||||
|
||||
public TestLegacyMainCirclePiece(string? priorityLookupPrefix)
|
||||
: base(priorityLookupPrefix, false)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
@ -20,13 +21,13 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
{
|
||||
new BeatmapStatistic
|
||||
{
|
||||
Name = @"Circle Count",
|
||||
Name = BeatmapsetsStrings.ShowStatsCountCircles,
|
||||
Content = circles.ToString(),
|
||||
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles),
|
||||
},
|
||||
new BeatmapStatistic
|
||||
{
|
||||
Name = @"Slider Count",
|
||||
Name = BeatmapsetsStrings.ShowStatsCountSliders,
|
||||
Content = sliders.ToString(),
|
||||
CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders),
|
||||
},
|
||||
|
@ -3,10 +3,10 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -16,63 +16,61 @@ using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyMainCirclePiece : CompositeDrawable
|
||||
{
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
|
||||
private readonly string priorityLookup;
|
||||
/// <summary>
|
||||
/// A prioritised prefix to perform texture lookups with.
|
||||
/// </summary>
|
||||
private readonly string? priorityLookupPrefix;
|
||||
|
||||
private readonly bool hasNumber;
|
||||
|
||||
public LegacyMainCirclePiece(string priorityLookup = null, bool hasNumber = true)
|
||||
protected Drawable CircleSprite = null!;
|
||||
protected Drawable OverlaySprite = null!;
|
||||
|
||||
protected Container OverlayLayer { get; private set; } = null!;
|
||||
|
||||
private SkinnableSpriteText hitCircleText = null!;
|
||||
|
||||
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private DrawableHitObject? drawableObject { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private ISkinSource skin { get; set; } = null!;
|
||||
|
||||
public LegacyMainCirclePiece(string? priorityLookupPrefix = null, bool hasNumber = true)
|
||||
{
|
||||
this.priorityLookup = priorityLookup;
|
||||
this.priorityLookupPrefix = priorityLookupPrefix;
|
||||
this.hasNumber = hasNumber;
|
||||
|
||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||
}
|
||||
|
||||
private Drawable hitCircleSprite;
|
||||
|
||||
protected Container OverlayLayer { get; private set; }
|
||||
|
||||
private Drawable hitCircleOverlay;
|
||||
private SkinnableSpriteText hitCircleText;
|
||||
|
||||
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||
|
||||
[Resolved]
|
||||
private DrawableHitObject drawableObject { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private ISkinSource skin { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var drawableOsuObject = (DrawableOsuHitObject)drawableObject;
|
||||
var drawableOsuObject = (DrawableOsuHitObject?)drawableObject;
|
||||
|
||||
bool allowFallback = false;
|
||||
|
||||
// attempt lookup using priority specification
|
||||
Texture baseTexture = getTextureWithFallback(string.Empty);
|
||||
|
||||
// if the base texture was not found without a fallback, switch on fallback mode and re-perform the lookup.
|
||||
if (baseTexture == null)
|
||||
{
|
||||
allowFallback = true;
|
||||
baseTexture = getTextureWithFallback(string.Empty);
|
||||
}
|
||||
// if a base texture for the specified prefix exists, continue using it for subsequent lookups.
|
||||
// otherwise fall back to the default prefix "hitcircle".
|
||||
string circleName = (priorityLookupPrefix != null && skin.GetTexture(priorityLookupPrefix) != null) ? priorityLookupPrefix : @"hitcircle";
|
||||
|
||||
// at this point, any further texture fetches should be correctly using the priority source if the base texture was retrieved using it.
|
||||
// the flow above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist.
|
||||
// expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png (potentially from the default/fall-through skin).
|
||||
// the conditional above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist.
|
||||
// expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png.
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
hitCircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = baseTexture })
|
||||
CircleSprite = new KiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName) })
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -81,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Child = hitCircleOverlay = new KiaiFlashingDrawable(() => getAnimationWithFallback(@"overlay", 1000 / 2d))
|
||||
Child = OverlaySprite = new KiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d))
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -105,39 +103,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
bool overlayAboveNumber = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true;
|
||||
|
||||
if (overlayAboveNumber)
|
||||
OverlayLayer.ChangeChildDepth(hitCircleOverlay, float.MinValue);
|
||||
OverlayLayer.ChangeChildDepth(OverlaySprite, float.MinValue);
|
||||
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||
|
||||
Texture getTextureWithFallback(string name)
|
||||
if (drawableOsuObject != null)
|
||||
{
|
||||
Texture tex = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(priorityLookup))
|
||||
{
|
||||
tex = skin.GetTexture($"{priorityLookup}{name}");
|
||||
|
||||
if (!allowFallback)
|
||||
return tex;
|
||||
}
|
||||
|
||||
return tex ?? skin.GetTexture($"hitcircle{name}");
|
||||
}
|
||||
|
||||
Drawable getAnimationWithFallback(string name, double frameLength)
|
||||
{
|
||||
Drawable animation = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(priorityLookup))
|
||||
{
|
||||
animation = skin.GetAnimation($"{priorityLookup}{name}", true, true, frameLength: frameLength);
|
||||
|
||||
if (!allowFallback)
|
||||
return animation;
|
||||
}
|
||||
|
||||
return animation ?? skin.GetAnimation($"hitcircle{name}", true, true, frameLength: frameLength);
|
||||
accentColour.BindTo(drawableOsuObject.AccentColour);
|
||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,28 +116,31 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||
accentColour.BindValueChanged(colour => CircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||
if (hasNumber)
|
||||
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
||||
|
||||
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||
updateStateTransforms(drawableObject, drawableObject.State.Value);
|
||||
if (drawableObject != null)
|
||||
{
|
||||
drawableObject.ApplyCustomUpdateState += updateStateTransforms;
|
||||
updateStateTransforms(drawableObject, drawableObject.State.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||
{
|
||||
const double legacy_fade_duration = 240;
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
using (BeginAbsoluteSequence(drawableObject.AsNonNull().HitStateUpdateTime))
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
hitCircleSprite.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
hitCircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||
CircleSprite.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
CircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||
|
||||
hitCircleOverlay.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
hitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||
OverlaySprite.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
OverlaySprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||
|
||||
if (hasNumber)
|
||||
{
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Collections
|
||||
});
|
||||
|
||||
Dependencies.Cache(manager);
|
||||
Dependencies.Cache(dialogOverlay);
|
||||
Dependencies.CacheAs<IDialogOverlay>(dialogOverlay);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
|
@ -1,44 +1,71 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Compose;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneComposeScreen : EditorClockTestScene
|
||||
{
|
||||
[Cached(typeof(EditorBeatmap))]
|
||||
[Cached(typeof(IBeatSnapProvider))]
|
||||
private readonly EditorBeatmap editorBeatmap =
|
||||
new EditorBeatmap(new OsuBeatmap
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
Ruleset = new OsuRuleset().RulesetInfo
|
||||
}
|
||||
});
|
||||
private EditorBeatmap editorBeatmap;
|
||||
|
||||
[Cached]
|
||||
private EditorClipboard clipboard = new EditorClipboard();
|
||||
|
||||
protected override void LoadComplete()
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
||||
|
||||
Child = new ComposeScreen
|
||||
AddStep("setup compose screen", () =>
|
||||
{
|
||||
State = { Value = Visibility.Visible },
|
||||
};
|
||||
var beatmap = new OsuBeatmap
|
||||
{
|
||||
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo }
|
||||
};
|
||||
|
||||
editorBeatmap = new EditorBeatmap(beatmap, new LegacyBeatmapSkin(beatmap.BeatmapInfo, null));
|
||||
|
||||
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
||||
|
||||
Child = new DependencyProvidingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CachedDependencies = new (Type, object)[]
|
||||
{
|
||||
(typeof(EditorBeatmap), editorBeatmap),
|
||||
(typeof(IBeatSnapProvider), editorBeatmap),
|
||||
},
|
||||
Child = new ComposeScreen { State = { Value = Visibility.Visible } },
|
||||
};
|
||||
});
|
||||
|
||||
AddUntilStep("wait for composer", () => this.ChildrenOfType<HitObjectComposer>().SingleOrDefault()?.IsLoaded == true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the skin of the edited beatmap is properly wrapped in a <see cref="LegacySkinTransformer"/>.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLegacyBeatmapSkinHasTransformer()
|
||||
{
|
||||
AddAssert("legacy beatmap skin has transformer", () =>
|
||||
{
|
||||
var sources = this.ChildrenOfType<BeatmapSkinProvidingContainer>().First().AllSources;
|
||||
return sources.OfType<LegacySkinTransformer>().Count(t => t.Skin == editorBeatmap.BeatmapSkin.AsNonNull().Skin) == 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Resolved]
|
||||
private SessionStatics sessionStatics { get; set; }
|
||||
|
||||
[Cached]
|
||||
[Cached(typeof(INotificationOverlay))]
|
||||
private readonly NotificationOverlay notificationOverlay;
|
||||
|
||||
[Cached]
|
||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
|
||||
private IntroScreen intro;
|
||||
|
||||
[Cached]
|
||||
[Cached(typeof(INotificationOverlay))]
|
||||
private NotificationOverlay notifications;
|
||||
|
||||
private ScheduledDelegate trackResetDelegate;
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -22,6 +23,17 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
[Resolved]
|
||||
private IRulesetStore rulesets { get; set; }
|
||||
|
||||
private readonly Mock<INotificationOverlay> notifications = new Mock<INotificationOverlay>();
|
||||
|
||||
private readonly BindableInt unreadNotificationCount = new BindableInt();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Dependencies.CacheAs(notifications.Object);
|
||||
notifications.SetupGet(n => n.UnreadCount).Returns(unreadNotificationCount);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
@ -31,10 +43,6 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
[Test]
|
||||
public void TestNotificationCounter()
|
||||
{
|
||||
ToolbarNotificationButton notificationButton = null;
|
||||
|
||||
AddStep("retrieve notification button", () => notificationButton = toolbar.ChildrenOfType<ToolbarNotificationButton>().Single());
|
||||
|
||||
setNotifications(1);
|
||||
setNotifications(2);
|
||||
setNotifications(3);
|
||||
@ -43,7 +51,7 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
|
||||
void setNotifications(int count)
|
||||
=> AddStep($"set notification count to {count}",
|
||||
() => notificationButton.NotificationCount.Value = count);
|
||||
() => unreadNotificationCount.Value = count);
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
|
@ -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 System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneFreeModSelectScreen : MultiplayerTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestFreeModSelect()
|
||||
{
|
||||
FreeModSelectScreen freeModSelectScreen = null;
|
||||
|
||||
AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen
|
||||
{
|
||||
State = { Value = Visibility.Visible }
|
||||
});
|
||||
|
||||
AddAssert("all visible mods are playable",
|
||||
() => this.ChildrenOfType<ModPanel>()
|
||||
.Where(panel => panel.IsPresent)
|
||||
.All(panel => panel.Mod.HasImplementation && panel.Mod.UserPlayable));
|
||||
|
||||
AddToggleStep("toggle visibility", visible =>
|
||||
{
|
||||
if (freeModSelectScreen != null)
|
||||
freeModSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
typeof(OsuLogo),
|
||||
typeof(IdleTracker),
|
||||
typeof(OnScreenDisplay),
|
||||
typeof(NotificationOverlay),
|
||||
typeof(INotificationOverlay),
|
||||
typeof(BeatmapListingOverlay),
|
||||
typeof(DashboardOverlay),
|
||||
typeof(NewsOverlay),
|
||||
@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
typeof(LoginOverlay),
|
||||
typeof(MusicController),
|
||||
typeof(AccountCreationOverlay),
|
||||
typeof(DialogOverlay),
|
||||
typeof(IDialogOverlay),
|
||||
typeof(ScreenshotManager)
|
||||
};
|
||||
|
||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
@ -113,12 +114,12 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddAssert("did not perform", () => !actionPerformed);
|
||||
AddAssert("only one exit attempt", () => blocker.ExitAttempts == 1);
|
||||
|
||||
AddUntilStep("wait for dialog display", () => Game.Dependencies.Get<DialogOverlay>().IsLoaded);
|
||||
waitForDialogOverlayLoad();
|
||||
|
||||
if (confirmed)
|
||||
{
|
||||
AddStep("accept dialog", () => InputManager.Key(Key.Number1));
|
||||
AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<DialogOverlay>().CurrentDialog == null);
|
||||
AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog == null);
|
||||
AddUntilStep("did perform", () => actionPerformed);
|
||||
}
|
||||
else
|
||||
@ -145,7 +146,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
AddWaitStep("wait a bit", 10);
|
||||
|
||||
AddUntilStep("wait for dialog display", () => Game.Dependencies.Get<DialogOverlay>().IsLoaded);
|
||||
waitForDialogOverlayLoad();
|
||||
|
||||
AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == blocker2);
|
||||
AddAssert("did not perform", () => !actionPerformed);
|
||||
@ -187,7 +188,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
AddWaitStep("wait a bit", 10);
|
||||
|
||||
AddUntilStep("wait for dialog display", () => Game.Dependencies.Get<DialogOverlay>().IsLoaded);
|
||||
waitForDialogOverlayLoad();
|
||||
|
||||
AddAssert("screen didn't change", () => Game.ScreenStack.CurrentScreen == screenWithNestedStack);
|
||||
AddAssert("nested screen didn't change", () => screenWithNestedStack.SubScreenStack.CurrentScreen == screenWithNestedStack.Blocker);
|
||||
@ -211,6 +212,8 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
}
|
||||
}
|
||||
|
||||
private void waitForDialogOverlayLoad() => AddUntilStep("wait for dialog overlay loaded", () => ((Drawable)Game.Dependencies.Get<IDialogOverlay>()).IsLoaded);
|
||||
|
||||
private void importAndWaitForSongSelect()
|
||||
{
|
||||
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
|
||||
@ -221,7 +224,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
public class DialogBlockingScreen : OsuScreen
|
||||
{
|
||||
[Resolved]
|
||||
private DialogOverlay dialogOverlay { get; set; }
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
private int dialogDisplayCount;
|
||||
|
||||
|
@ -6,6 +6,7 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Screens;
|
||||
@ -200,10 +201,10 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
AddStep("choose clear all scores", () => InputManager.Key(Key.Number4));
|
||||
|
||||
AddUntilStep("wait for dialog display", () => Game.Dependencies.Get<DialogOverlay>().IsLoaded);
|
||||
AddUntilStep("wait for dialog", () => Game.Dependencies.Get<DialogOverlay>().CurrentDialog != null);
|
||||
AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get<IDialogOverlay>()).IsLoaded);
|
||||
AddUntilStep("wait for dialog", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog != null);
|
||||
AddStep("confirm deletion", () => InputManager.Key(Key.Number1));
|
||||
AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<DialogOverlay>().CurrentDialog == null);
|
||||
AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog == null);
|
||||
|
||||
AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find<ScoreInfo>(score.ID)?.DeletePending == true));
|
||||
|
||||
@ -246,10 +247,10 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for dialog display", () => Game.Dependencies.Get<DialogOverlay>().IsLoaded);
|
||||
AddUntilStep("wait for dialog", () => Game.Dependencies.Get<DialogOverlay>().CurrentDialog != null);
|
||||
AddUntilStep("wait for dialog display", () => ((Drawable)Game.Dependencies.Get<IDialogOverlay>()).IsLoaded);
|
||||
AddUntilStep("wait for dialog", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog != null);
|
||||
AddStep("confirm deletion", () => InputManager.Key(Key.Number1));
|
||||
AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<DialogOverlay>().CurrentDialog == null);
|
||||
AddUntilStep("wait for dialog dismissed", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog == null);
|
||||
|
||||
AddUntilStep("ensure score is pending deletion", () => Game.Realm.Run(r => r.Find<ScoreInfo>(score.ID)?.DeletePending == true));
|
||||
|
||||
|
188
osu.Game.Tests/Visual/Online/TestSceneChannelList.cs
Normal file
188
osu.Game.Tests/Visual/Online/TestSceneChannelList.cs
Normal file
@ -0,0 +1,188 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Chat.ChannelList;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneChannelList : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
|
||||
|
||||
[Cached]
|
||||
private readonly Bindable<Channel> selected = new Bindable<Channel>();
|
||||
|
||||
private OsuSpriteText selectorText;
|
||||
private OsuSpriteText selectedText;
|
||||
private OsuSpriteText leaveText;
|
||||
private ChannelList channelList;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
Child = new GridContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.7f,
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.Absolute, 20),
|
||||
new Dimension(GridSizeMode.Absolute, 20),
|
||||
new Dimension(GridSizeMode.Absolute, 20),
|
||||
new Dimension(),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
selectorText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
},
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
selectedText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
},
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
leaveText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
},
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
channelList = new ChannelList
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 190,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
channelList.OnRequestSelect += channel =>
|
||||
{
|
||||
channelList.SelectorActive.Value = false;
|
||||
selected.Value = channel;
|
||||
};
|
||||
|
||||
channelList.OnRequestLeave += channel =>
|
||||
{
|
||||
leaveText.Text = $"OnRequestLeave: {channel.Name}";
|
||||
leaveText.FadeOutFromOne(1000, Easing.InQuint);
|
||||
selected.Value = null;
|
||||
channelList.RemoveChannel(channel);
|
||||
};
|
||||
|
||||
channelList.SelectorActive.BindValueChanged(change =>
|
||||
{
|
||||
selectorText.Text = $"Channel Selector Active: {change.NewValue}";
|
||||
selected.Value = null;
|
||||
}, true);
|
||||
|
||||
selected.BindValueChanged(change =>
|
||||
{
|
||||
selectedText.Text = $"Selected Channel: {change.NewValue?.Name ?? "[null]"}";
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("Add Public Channels", () =>
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
channelList.AddChannel(createRandomPublicChannel());
|
||||
});
|
||||
|
||||
AddStep("Add Private Channels", () =>
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
channelList.AddChannel(createRandomPrivateChannel());
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestVisual()
|
||||
{
|
||||
AddStep("Unread Selected", () =>
|
||||
{
|
||||
if (selected.Value != null)
|
||||
channelList.GetItem(selected.Value).Unread.Value = true;
|
||||
});
|
||||
|
||||
AddStep("Read Selected", () =>
|
||||
{
|
||||
if (selected.Value != null)
|
||||
channelList.GetItem(selected.Value).Unread.Value = false;
|
||||
});
|
||||
|
||||
AddStep("Add Mention Selected", () =>
|
||||
{
|
||||
if (selected.Value != null)
|
||||
channelList.GetItem(selected.Value).Mentions.Value++;
|
||||
});
|
||||
|
||||
AddStep("Add 98 Mentions Selected", () =>
|
||||
{
|
||||
if (selected.Value != null)
|
||||
channelList.GetItem(selected.Value).Mentions.Value += 98;
|
||||
});
|
||||
|
||||
AddStep("Clear Mentions Selected", () =>
|
||||
{
|
||||
if (selected.Value != null)
|
||||
channelList.GetItem(selected.Value).Mentions.Value = 0;
|
||||
});
|
||||
}
|
||||
|
||||
private Channel createRandomPublicChannel()
|
||||
{
|
||||
int id = RNG.Next(0, 10000);
|
||||
return new Channel
|
||||
{
|
||||
Name = $"#channel-{id}",
|
||||
Type = ChannelType.Public,
|
||||
Id = id,
|
||||
};
|
||||
}
|
||||
|
||||
private Channel createRandomPrivateChannel()
|
||||
{
|
||||
int id = RNG.Next(0, 10000);
|
||||
return new Channel(new APIUser
|
||||
{
|
||||
Id = id,
|
||||
Username = $"test user {id}",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,163 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
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.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Chat.ChannelList;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneChannelListItem : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
|
||||
|
||||
[Cached]
|
||||
private readonly Bindable<Channel> selected = new Bindable<Channel>();
|
||||
|
||||
private static readonly List<Channel> channels = new List<Channel>
|
||||
{
|
||||
createPublicChannel("#public-channel"),
|
||||
createPublicChannel("#public-channel-long-name"),
|
||||
createPrivateChannel("test user", 2),
|
||||
createPrivateChannel("test user long name", 3),
|
||||
};
|
||||
|
||||
private readonly Dictionary<Channel, ChannelListItem> channelMap = new Dictionary<Channel, ChannelListItem>();
|
||||
|
||||
private FillFlowContainer flow;
|
||||
private OsuSpriteText selectedText;
|
||||
private OsuSpriteText leaveText;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
foreach (var item in channelMap.Values)
|
||||
item.Expire();
|
||||
|
||||
channelMap.Clear();
|
||||
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
selectedText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
},
|
||||
leaveText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Height = 16,
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 190,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background6,
|
||||
},
|
||||
flow = new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
selected.BindValueChanged(change =>
|
||||
{
|
||||
selectedText.Text = $"Selected Channel: {change.NewValue?.Name ?? "[null]"}";
|
||||
}, true);
|
||||
|
||||
foreach (var channel in channels)
|
||||
{
|
||||
var item = new ChannelListItem(channel);
|
||||
flow.Add(item);
|
||||
channelMap.Add(channel, item);
|
||||
item.OnRequestSelect += c => selected.Value = c;
|
||||
item.OnRequestLeave += leaveChannel;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestVisual()
|
||||
{
|
||||
AddStep("Select second item", () => selected.Value = channels.Skip(1).First());
|
||||
|
||||
AddStep("Unread Selected", () =>
|
||||
{
|
||||
if (selected.Value != null)
|
||||
channelMap[selected.Value].Unread.Value = true;
|
||||
});
|
||||
|
||||
AddStep("Read Selected", () =>
|
||||
{
|
||||
if (selected.Value != null)
|
||||
channelMap[selected.Value].Unread.Value = false;
|
||||
});
|
||||
|
||||
AddStep("Add Mention Selected", () =>
|
||||
{
|
||||
if (selected.Value != null)
|
||||
channelMap[selected.Value].Mentions.Value++;
|
||||
});
|
||||
|
||||
AddStep("Add 98 Mentions Selected", () =>
|
||||
{
|
||||
if (selected.Value != null)
|
||||
channelMap[selected.Value].Mentions.Value += 98;
|
||||
});
|
||||
|
||||
AddStep("Clear Mentions Selected", () =>
|
||||
{
|
||||
if (selected.Value != null)
|
||||
channelMap[selected.Value].Mentions.Value = 0;
|
||||
});
|
||||
}
|
||||
|
||||
private void leaveChannel(Channel channel)
|
||||
{
|
||||
leaveText.Text = $"OnRequestLeave: {channel.Name}";
|
||||
leaveText.FadeOutFromOne(1000, Easing.InQuint);
|
||||
}
|
||||
|
||||
private static Channel createPublicChannel(string name) =>
|
||||
new Channel { Name = name, Type = ChannelType.Public, Id = 1234 };
|
||||
|
||||
private static Channel createPrivateChannel(string username, int id)
|
||||
=> new Channel(new APIUser { Id = id, Username = username });
|
||||
}
|
||||
}
|
@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Dependencies.Cache(chatManager);
|
||||
|
||||
Dependencies.Cache(new ChatOverlay());
|
||||
Dependencies.Cache(dialogOverlay);
|
||||
Dependencies.CacheAs<IDialogOverlay>(dialogOverlay);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
|
@ -200,7 +200,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
[Cached]
|
||||
public ChannelManager ChannelManager { get; } = new ChannelManager();
|
||||
|
||||
[Cached]
|
||||
[Cached(typeof(INotificationOverlay))]
|
||||
public NotificationOverlay NotificationOverlay { get; } = new NotificationOverlay
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
{
|
||||
public class TestSceneMigrationScreens : ScreenTestScene
|
||||
{
|
||||
[Cached]
|
||||
[Cached(typeof(INotificationOverlay))]
|
||||
private readonly NotificationOverlay notifications;
|
||||
|
||||
public TestSceneMigrationScreens()
|
||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
Depth = -1
|
||||
});
|
||||
|
||||
Dependencies.Cache(dialogOverlay);
|
||||
Dependencies.CacheAs<IDialogOverlay>(dialogOverlay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
@ -55,7 +56,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
AddStep("no mods selected", () => SelectedMods.Value = Array.Empty<Mod>());
|
||||
|
||||
AddAssert("first bar text is Circle Size", () => advancedStats.ChildrenOfType<SpriteText>().First().Text == "Circle Size");
|
||||
AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType<SpriteText>().First().Text == BeatmapsetsStrings.ShowStatsCs);
|
||||
AddAssert("circle size bar is white", () => barIsWhite(advancedStats.FirstValue));
|
||||
AddAssert("HP drain bar is white", () => barIsWhite(advancedStats.HpDrain));
|
||||
AddAssert("accuracy bar is white", () => barIsWhite(advancedStats.Accuracy));
|
||||
@ -78,7 +79,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
StarRating = 8
|
||||
});
|
||||
|
||||
AddAssert("first bar text is Key Count", () => advancedStats.ChildrenOfType<SpriteText>().First().Text == "Key Count");
|
||||
AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType<SpriteText>().First().Text == BeatmapsetsStrings.ShowStatsCsMania);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
{
|
||||
private readonly FailableLeaderboard leaderboard;
|
||||
|
||||
[Cached]
|
||||
[Cached(typeof(IDialogOverlay))]
|
||||
private readonly DialogOverlay dialogOverlay;
|
||||
|
||||
private ScoreManager scoreManager;
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
{
|
||||
public class TestSceneUserTopScoreContainer : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
[Cached(typeof(IDialogOverlay))]
|
||||
private readonly DialogOverlay dialogOverlay;
|
||||
|
||||
public TestSceneUserTopScoreContainer()
|
||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
private BeatmapInfo beatmapInfo;
|
||||
|
||||
[Cached]
|
||||
[Cached(typeof(IDialogOverlay))]
|
||||
private readonly DialogOverlay dialogOverlay;
|
||||
|
||||
public TestSceneDeleteLocalScore()
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
[Resolved]
|
||||
private RulesetStore rulesetStore { get; set; }
|
||||
|
||||
private ModSelectScreen modSelectScreen;
|
||||
private UserModSelectScreen modSelectScreen;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
private void createScreen()
|
||||
{
|
||||
AddStep("create screen", () => Child = modSelectScreen = new ModSelectScreen
|
||||
AddStep("create screen", () => Child = modSelectScreen = new UserModSelectScreen
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
State = { Value = Visibility.Visible },
|
||||
|
@ -10,19 +10,19 @@ using osu.Game.Overlays;
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestScenePopupScreenTitle : OsuTestScene
|
||||
public class TestSceneShearedOverlayHeader : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||
|
||||
[Test]
|
||||
public void TestPopupScreenTitle()
|
||||
public void TestShearedOverlayHeader()
|
||||
{
|
||||
AddStep("create content", () =>
|
||||
{
|
||||
Child = new PopupScreenTitle
|
||||
Child = new ShearedOverlayHeader
|
||||
{
|
||||
Title = "Popup Screen Title",
|
||||
Title = "Sheared overlay header",
|
||||
Description = string.Join(" ", Enumerable.Repeat("This is a description.", 20)),
|
||||
Close = () => { }
|
||||
};
|
||||
@ -34,9 +34,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
AddStep("create content", () =>
|
||||
{
|
||||
Child = new PopupScreenTitle
|
||||
Child = new ShearedOverlayHeader
|
||||
{
|
||||
Title = "Popup Screen Title",
|
||||
Title = "Sheared overlay header",
|
||||
Description = "This is a description."
|
||||
};
|
||||
});
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -14,6 +15,6 @@ namespace osu.Game.Beatmaps
|
||||
public Func<Drawable> CreateIcon;
|
||||
|
||||
public string Content;
|
||||
public string Name;
|
||||
public LocalisableString Name;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Beatmaps.Drawables
|
||||
{
|
||||
@ -104,7 +105,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
if ((beatmapSet as IBeatmapSetOnlineInfo)?.Availability.DownloadDisabled == true)
|
||||
{
|
||||
button.Enabled.Value = false;
|
||||
button.TooltipText = "this beatmap is currently not available for download.";
|
||||
button.TooltipText = BeatmapsetsStrings.AvailabilityDisabled;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -158,7 +158,7 @@ namespace osu.Game.Collections
|
||||
public Func<Vector2, bool> IsTextBoxHovered;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private DialogOverlay dialogOverlay { get; set; }
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private CollectionManager collectionManager { get; set; }
|
||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Database
|
||||
private OsuConfigManager config { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private NotificationOverlay notificationOverlay { get; set; } = null!;
|
||||
private INotificationOverlay notificationOverlay { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuGame game { get; set; } = null!;
|
||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Database
|
||||
private OsuGame game { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private DialogOverlay dialogOverlay { get; set; }
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private DesktopGameHost desktopGameHost { get; set; }
|
||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Graphics
|
||||
private Storage storage;
|
||||
|
||||
[Resolved]
|
||||
private NotificationOverlay notificationOverlay { get; set; }
|
||||
private INotificationOverlay notificationOverlay { get; set; }
|
||||
|
||||
private Sample shutter;
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@ -15,7 +16,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
}
|
||||
|
||||
public OsuMenuItem(string text, MenuItemType type, Action action)
|
||||
public OsuMenuItem(LocalisableString text, MenuItemType type, Action action)
|
||||
: base(text, action)
|
||||
{
|
||||
Type = type;
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface.PageSelector
|
||||
{
|
||||
@ -29,7 +30,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
previousPageButton = new PageSelectorPrevNextButton(false, "prev")
|
||||
previousPageButton = new PageSelectorPrevNextButton(false, CommonStrings.PaginationPrevious)
|
||||
{
|
||||
Action = () => CurrentPage.Value -= 1,
|
||||
},
|
||||
@ -38,7 +39,7 @@ namespace osu.Game.Graphics.UserInterface.PageSelector
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
},
|
||||
nextPageButton = new PageSelectorPrevNextButton(true, "next")
|
||||
nextPageButton = new PageSelectorPrevNextButton(true, CommonStrings.PaginationNext)
|
||||
{
|
||||
Action = () => CurrentPage.Value += 1
|
||||
}
|
||||
|
@ -2,9 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK;
|
||||
|
||||
@ -13,12 +15,12 @@ namespace osu.Game.Graphics.UserInterface.PageSelector
|
||||
public class PageSelectorPrevNextButton : PageSelectorButton
|
||||
{
|
||||
private readonly bool rightAligned;
|
||||
private readonly string text;
|
||||
private readonly LocalisableString text;
|
||||
|
||||
private SpriteIcon icon;
|
||||
private OsuSpriteText name;
|
||||
|
||||
public PageSelectorPrevNextButton(bool rightAligned, string text)
|
||||
public PageSelectorPrevNextButton(bool rightAligned, LocalisableString text)
|
||||
{
|
||||
this.rightAligned = rightAligned;
|
||||
this.text = text;
|
||||
|
@ -5,6 +5,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -27,7 +28,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
});
|
||||
|
||||
TextFlow.Padding = new MarginPadding { Right = 35 };
|
||||
PlaceholderText = "type to search";
|
||||
PlaceholderText = HomeStrings.SearchPlaceholder;
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
|
||||
|
@ -19,8 +19,10 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public class PopupScreenTitle : CompositeDrawable
|
||||
public class ShearedOverlayHeader : CompositeDrawable
|
||||
{
|
||||
public const float HEIGHT = main_area_height + 2 * corner_radius;
|
||||
|
||||
public LocalisableString Title
|
||||
{
|
||||
set => titleSpriteText.Text = value;
|
||||
@ -48,7 +50,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
private readonly OsuTextFlowContainer descriptionText;
|
||||
private readonly IconButton closeButton;
|
||||
|
||||
public PopupScreenTitle()
|
||||
public ShearedOverlayHeader()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
@ -67,7 +69,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
underlayContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = main_area_height + 2 * corner_radius,
|
||||
Height = HEIGHT,
|
||||
CornerRadius = corner_radius,
|
||||
Masking = true,
|
||||
BorderThickness = 2,
|
@ -11,7 +11,9 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
@ -80,7 +82,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
|
||||
Text = "show more".ToUpper(),
|
||||
Text = CommonStrings.ButtonsShowMore.ToUpper(),
|
||||
},
|
||||
rightIcon = new ChevronIcon
|
||||
{
|
||||
|
@ -14,6 +14,7 @@ using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterfaceV2
|
||||
@ -139,7 +140,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
|
||||
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem("Delete", MenuItemType.Destructive, () => DeleteRequested?.Invoke())
|
||||
new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => DeleteRequested?.Invoke())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -9,16 +9,6 @@ namespace osu.Game.Localisation
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.Common";
|
||||
|
||||
/// <summary>
|
||||
/// "Cancel"
|
||||
/// </summary>
|
||||
public static LocalisableString Cancel => new TranslatableString(getKey(@"cancel"), @"Cancel");
|
||||
|
||||
/// <summary>
|
||||
/// "Clear"
|
||||
/// </summary>
|
||||
public static LocalisableString Clear => new TranslatableString(getKey(@"clear"), @"Clear");
|
||||
|
||||
/// <summary>
|
||||
/// "Enabled"
|
||||
/// </summary>
|
||||
|
30
osu.Game/Localisation/DebugLocalisationStore.cs
Normal file
30
osu.Game/Localisation/DebugLocalisationStore.cs
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation
|
||||
{
|
||||
public class DebugLocalisationStore : ILocalisationStore
|
||||
{
|
||||
public string Get(string lookup) => $@"[[{lookup.Substring(lookup.LastIndexOf('.') + 1)}]]";
|
||||
|
||||
public Task<string> GetAsync(string lookup, CancellationToken cancellationToken = default) => Task.FromResult(Get(lookup));
|
||||
|
||||
public Stream GetStream(string name) => throw new NotImplementedException();
|
||||
|
||||
public IEnumerable<string> GetAvailableResources() => throw new NotImplementedException();
|
||||
|
||||
public CultureInfo EffectiveCulture { get; } = CultureInfo.CurrentCulture;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -110,6 +110,11 @@ namespace osu.Game.Localisation
|
||||
// zh_hk,
|
||||
|
||||
[Description(@"繁體中文(台灣)")]
|
||||
zh_hant
|
||||
zh_hant,
|
||||
|
||||
#if DEBUG
|
||||
[Description(@"Debug (show raw keys)")]
|
||||
debug
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ namespace osu.Game.Online.API.Requests
|
||||
|
||||
private readonly BeatmapSetType type;
|
||||
|
||||
public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int page = 0, int itemsPerPage = 6)
|
||||
: base(page, itemsPerPage)
|
||||
public GetUserBeatmapsRequest(long userId, BeatmapSetType type, PaginationParameters pagination)
|
||||
: base(pagination)
|
||||
{
|
||||
this.userId = userId;
|
||||
this.type = type;
|
||||
|
@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
private readonly long userId;
|
||||
|
||||
public GetUserKudosuHistoryRequest(long userId, int page = 0, int itemsPerPage = 5)
|
||||
: base(page, itemsPerPage)
|
||||
public GetUserKudosuHistoryRequest(long userId, PaginationParameters pagination)
|
||||
: base(pagination)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
private readonly long userId;
|
||||
|
||||
public GetUserMostPlayedBeatmapsRequest(long userId, int page = 0, int itemsPerPage = 5)
|
||||
: base(page, itemsPerPage)
|
||||
public GetUserMostPlayedBeatmapsRequest(long userId, PaginationParameters pagination)
|
||||
: base(pagination)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
private readonly long userId;
|
||||
|
||||
public GetUserRecentActivitiesRequest(long userId, int page = 0, int itemsPerPage = 5)
|
||||
: base(page, itemsPerPage)
|
||||
public GetUserRecentActivitiesRequest(long userId, PaginationParameters pagination)
|
||||
: base(pagination)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ namespace osu.Game.Online.API.Requests
|
||||
private readonly ScoreType type;
|
||||
private readonly RulesetInfo ruleset;
|
||||
|
||||
public GetUserScoresRequest(long userId, ScoreType type, int page = 0, int itemsPerPage = 5, RulesetInfo ruleset = null)
|
||||
: base(page, itemsPerPage)
|
||||
public GetUserScoresRequest(long userId, ScoreType type, PaginationParameters pagination, RulesetInfo ruleset = null)
|
||||
: base(pagination)
|
||||
{
|
||||
this.userId = userId;
|
||||
this.type = type;
|
||||
|
@ -8,21 +8,19 @@ namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public abstract class PaginatedAPIRequest<T> : APIRequest<T> where T : class
|
||||
{
|
||||
private readonly int page;
|
||||
private readonly int itemsPerPage;
|
||||
private readonly PaginationParameters pagination;
|
||||
|
||||
protected PaginatedAPIRequest(int page, int itemsPerPage)
|
||||
protected PaginatedAPIRequest(PaginationParameters pagination)
|
||||
{
|
||||
this.page = page;
|
||||
this.itemsPerPage = itemsPerPage;
|
||||
this.pagination = pagination;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
|
||||
req.AddParameter("offset", (page * itemsPerPage).ToString(CultureInfo.InvariantCulture));
|
||||
req.AddParameter("limit", itemsPerPage.ToString(CultureInfo.InvariantCulture));
|
||||
req.AddParameter("offset", pagination.Offset.ToString(CultureInfo.InvariantCulture));
|
||||
req.AddParameter("limit", pagination.Limit.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
return req;
|
||||
}
|
||||
|
38
osu.Game/Online/API/Requests/PaginationParameters.cs
Normal file
38
osu.Game/Online/API/Requests/PaginationParameters.cs
Normal file
@ -0,0 +1,38 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a pagination data used for <see cref="PaginatedAPIRequest{T}"/>.
|
||||
/// </summary>
|
||||
public readonly struct PaginationParameters
|
||||
{
|
||||
/// <summary>
|
||||
/// The starting point of the request.
|
||||
/// </summary>
|
||||
public int Offset { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of items to return in this request.
|
||||
/// </summary>
|
||||
public int Limit { get; }
|
||||
|
||||
public PaginationParameters(int offset, int limit)
|
||||
{
|
||||
Offset = offset;
|
||||
Limit = limit;
|
||||
}
|
||||
|
||||
public PaginationParameters(int limit)
|
||||
: this(0, limit)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="PaginationParameters"/> of the next number of items defined by <paramref name="limit"/> after this.
|
||||
/// </summary>
|
||||
/// <param name="limit">The limit of the next pagination.</param>
|
||||
public PaginationParameters TakeNext(int limit) => new PaginationParameters(Offset + Limit, limit);
|
||||
}
|
||||
}
|
@ -1,22 +1,23 @@
|
||||
// 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.ComponentModel;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public enum APIPlayStyle
|
||||
{
|
||||
[Description("Keyboard")]
|
||||
[LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceKeyboard))]
|
||||
Keyboard,
|
||||
|
||||
[Description("Mouse")]
|
||||
[LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceMouse))]
|
||||
Mouse,
|
||||
|
||||
[Description("Tablet")]
|
||||
[LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceTablet))]
|
||||
Tablet,
|
||||
|
||||
[Description("Touch Screen")]
|
||||
[LocalisableDescription(typeof(CommonStrings), nameof(CommonStrings.DeviceTouch))]
|
||||
Touch,
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Online.Chat
|
||||
private GameHost host { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private DialogOverlay dialogOverlay { get; set; }
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
private Bindable<bool> externalLinkWarning;
|
||||
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Online.Chat
|
||||
public class MessageNotifier : Component
|
||||
{
|
||||
[Resolved]
|
||||
private NotificationOverlay notifications { get; set; }
|
||||
private INotificationOverlay notifications { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private ChatOverlay chatOverlay { get; set; }
|
||||
@ -170,7 +170,7 @@ namespace osu.Game.Online.Chat
|
||||
public override bool IsImportant => false;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, ChatOverlay chatOverlay, NotificationOverlay notificationOverlay)
|
||||
private void load(OsuColour colours, ChatOverlay chatOverlay, INotificationOverlay notificationOverlay)
|
||||
{
|
||||
IconBackground.Colour = colours.PurpleDark;
|
||||
|
||||
|
@ -12,6 +12,7 @@ using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Chat;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
@ -63,7 +64,7 @@ namespace osu.Game.Online.Chat
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = text_box_height,
|
||||
PlaceholderText = "type your message",
|
||||
PlaceholderText = ChatStrings.InputPlaceholder,
|
||||
CornerRadius = corner_radius,
|
||||
ReleaseFocusOnCommit = false,
|
||||
HoldFocus = true,
|
||||
|
@ -30,6 +30,7 @@ using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Online.Leaderboards
|
||||
@ -64,7 +65,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
private List<ScoreComponentLabel> statisticsLabels;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private DialogOverlay dialogOverlay { get; set; }
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private SongSelect songSelect { get; set; }
|
||||
@ -291,8 +292,8 @@ namespace osu.Game.Online.Leaderboards
|
||||
|
||||
protected virtual IEnumerable<LeaderboardScoreStatistic> GetStatistics(ScoreInfo model) => new[]
|
||||
{
|
||||
new LeaderboardScoreStatistic(FontAwesome.Solid.Link, "Max Combo", model.MaxCombo.ToString()),
|
||||
new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", model.DisplayAccuracy)
|
||||
new LeaderboardScoreStatistic(FontAwesome.Solid.Link, BeatmapsetsStrings.ShowScoreboardHeadersCombo, model.MaxCombo.ToString()),
|
||||
new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, BeatmapsetsStrings.ShowScoreboardHeadersAccuracy, model.DisplayAccuracy)
|
||||
};
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
@ -403,9 +404,9 @@ namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
public IconUsage Icon;
|
||||
public LocalisableString Value;
|
||||
public string Name;
|
||||
public LocalisableString Name;
|
||||
|
||||
public LeaderboardScoreStatistic(IconUsage icon, string name, LocalisableString value)
|
||||
public LeaderboardScoreStatistic(IconUsage icon, LocalisableString name, LocalisableString value)
|
||||
{
|
||||
Icon = icon;
|
||||
Name = name;
|
||||
@ -426,7 +427,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => new LegacyScoreExporter(storage).Export(Score)));
|
||||
|
||||
if (!isOnlineScope)
|
||||
items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score))));
|
||||
items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score))));
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
// 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.ComponentModel;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Online.Rooms
|
||||
{
|
||||
@ -11,10 +12,10 @@ namespace osu.Game.Online.Rooms
|
||||
|
||||
Playlists,
|
||||
|
||||
[Description("Head to head")]
|
||||
[LocalisableDescription(typeof(MatchesStrings), nameof(MatchesStrings.MatchTeamTypesHeadToHead))]
|
||||
HeadToHead,
|
||||
|
||||
[Description("Team VS")]
|
||||
[LocalisableDescription(typeof(MatchesStrings), nameof(MatchesStrings.MatchTeamTypesTeamVs))]
|
||||
TeamVersus,
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game
|
||||
/// The full osu! experience. Builds on top of <see cref="OsuGameBase"/> to add menus and binding logic
|
||||
/// for initial components that are generally retrieved via DI.
|
||||
/// </summary>
|
||||
public class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>, ILocalUserPlayInfo
|
||||
public class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>, ILocalUserPlayInfo, IPerformFromScreenRunner
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications).
|
||||
@ -586,12 +586,6 @@ namespace osu.Game
|
||||
|
||||
private PerformFromMenuRunner performFromMainMenuTask;
|
||||
|
||||
/// <summary>
|
||||
/// Perform an action only after returning to a specific screen as indicated by <paramref name="validScreens"/>.
|
||||
/// Eagerly tries to exit the current screen until it succeeds.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to perform once we are in the correct state.</param>
|
||||
/// <param name="validScreens">An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. <see cref="MainMenu"/> is used if not specified.</param>
|
||||
public void PerformFromScreen(Action<IScreen> action, IEnumerable<Type> validScreens = null)
|
||||
{
|
||||
performFromMainMenuTask?.Cancel();
|
||||
@ -634,6 +628,14 @@ namespace osu.Game
|
||||
|
||||
foreach (var language in Enum.GetValues(typeof(Language)).OfType<Language>())
|
||||
{
|
||||
#if DEBUG
|
||||
if (language == Language.debug)
|
||||
{
|
||||
Localisation.AddLanguage(Language.debug.ToString(), new DebugLocalisationStore());
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
string cultureCode = language.ToCultureCode();
|
||||
|
||||
try
|
||||
@ -778,7 +780,7 @@ namespace osu.Game
|
||||
|
||||
loadComponentSingleFile(onScreenDisplay, Add, true);
|
||||
|
||||
loadComponentSingleFile(Notifications.With(d =>
|
||||
loadComponentSingleFile<INotificationOverlay>(Notifications.With(d =>
|
||||
{
|
||||
d.Anchor = Anchor.TopRight;
|
||||
d.Origin = Anchor.TopRight;
|
||||
@ -825,7 +827,7 @@ namespace osu.Game
|
||||
}, rightFloatingOverlayContent.Add, true);
|
||||
|
||||
loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true);
|
||||
loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true);
|
||||
loadComponentSingleFile<IDialogOverlay>(new DialogOverlay(), topMostOverlayContent.Add, true);
|
||||
|
||||
loadComponentSingleFile(CreateHighPerformanceSession(), Add);
|
||||
|
||||
@ -982,12 +984,14 @@ namespace osu.Game
|
||||
/// <param name="component">The component to load.</param>
|
||||
/// <param name="loadCompleteAction">An action to invoke on load completion (generally to add the component to the hierarchy).</param>
|
||||
/// <param name="cache">Whether to cache the component as type <typeparamref name="T"/> into the game dependencies before any scheduling.</param>
|
||||
private T loadComponentSingleFile<T>(T component, Action<T> loadCompleteAction, bool cache = false)
|
||||
where T : Drawable
|
||||
private T loadComponentSingleFile<T>(T component, Action<Drawable> loadCompleteAction, bool cache = false)
|
||||
where T : class
|
||||
{
|
||||
if (cache)
|
||||
dependencies.CacheAs(component);
|
||||
|
||||
var drawableComponent = component as Drawable ?? throw new ArgumentException($"Component must be a {nameof(Drawable)}", nameof(component));
|
||||
|
||||
if (component is OsuFocusedOverlayContainer overlay)
|
||||
focusedOverlays.Add(overlay);
|
||||
|
||||
@ -1011,7 +1015,7 @@ namespace osu.Game
|
||||
// Since this is running in a separate thread, it is possible for OsuGame to be disposed after LoadComponentAsync has been called
|
||||
// throwing an exception. To avoid this, the call is scheduled on the update thread, which does not run if IsDisposed = true
|
||||
Task task = null;
|
||||
var del = new ScheduledDelegate(() => task = LoadComponentAsync(component, loadCompleteAction));
|
||||
var del = new ScheduledDelegate(() => task = LoadComponentAsync(drawableComponent, loadCompleteAction));
|
||||
Scheduler.Add(del);
|
||||
|
||||
// The delegate won't complete if OsuGame has been disposed in the meantime
|
||||
|
@ -16,6 +16,7 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -68,7 +69,7 @@ namespace osu.Game.Overlays.AccountCreation
|
||||
},
|
||||
usernameTextBox = new OsuTextBox
|
||||
{
|
||||
PlaceholderText = "username",
|
||||
PlaceholderText = UsersStrings.LoginUsername,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
TabbableContentContainer = this
|
||||
},
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
@ -69,14 +70,14 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
textContainer.Clear();
|
||||
textContainer.AddParagraph(downloadDisabled
|
||||
? "This beatmap is currently not available for download."
|
||||
: "Portions of this beatmap have been removed at the request of the creator or a third-party rights holder.", t => t.Colour = Color4.Orange);
|
||||
? BeatmapsetsStrings.AvailabilityDisabled
|
||||
: BeatmapsetsStrings.AvailabilityPartsRemoved, t => t.Colour = Color4.Orange);
|
||||
|
||||
if (hasExternalLink)
|
||||
{
|
||||
textContainer.NewParagraph();
|
||||
textContainer.NewParagraph();
|
||||
textContainer.AddLink("Check here for more information.", BeatmapSet.Availability.ExternalLink, creationParameters: t => t.Font = OsuFont.GetFont(size: 10));
|
||||
textContainer.AddLink(BeatmapsetsStrings.AvailabilityMoreInfo, BeatmapSet.Availability.ExternalLink, creationParameters: t => t.Font = OsuFont.GetFont(size: 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(IAPIProvider api, NotificationOverlay notifications)
|
||||
private void load(IAPIProvider api, INotificationOverlay notifications)
|
||||
{
|
||||
SpriteIcon icon;
|
||||
|
||||
|
@ -7,6 +7,7 @@ using osu.Game.Graphics.Sprites;
|
||||
using osuTK;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
{
|
||||
@ -28,7 +29,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = @"You need to be an osu!supporter to access the friend and country rankings!",
|
||||
Text = BeatmapsetsStrings.ShowScoreboardSupporterOnly,
|
||||
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
|
||||
},
|
||||
text = new LinkFlowContainer(t => t.Font = t.Font.With(size: 11))
|
||||
|
135
osu.Game/Overlays/Chat/ChannelList/ChannelList.cs
Normal file
135
osu.Game/Overlays/Chat/ChannelList/ChannelList.cs
Normal file
@ -0,0 +1,135 @@
|
||||
// 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.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Chat;
|
||||
|
||||
namespace osu.Game.Overlays.Chat.ChannelList
|
||||
{
|
||||
public class ChannelList : Container
|
||||
{
|
||||
public Action<Channel>? OnRequestSelect;
|
||||
public Action<Channel>? OnRequestLeave;
|
||||
|
||||
public readonly BindableBool SelectorActive = new BindableBool();
|
||||
|
||||
private readonly Dictionary<Channel, ChannelListItem> channelMap = new Dictionary<Channel, ChannelListItem>();
|
||||
|
||||
private ChannelListItemFlow publicChannelFlow = null!;
|
||||
private ChannelListItemFlow privateChannelFlow = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background6,
|
||||
},
|
||||
new OsuScrollContainer
|
||||
{
|
||||
Padding = new MarginPadding { Vertical = 7 },
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarAnchor = Anchor.TopRight,
|
||||
ScrollDistance = 35f,
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
publicChannelFlow = new ChannelListItemFlow("CHANNELS"),
|
||||
new ChannelListSelector
|
||||
{
|
||||
Margin = new MarginPadding { Bottom = 10 },
|
||||
SelectorActive = { BindTarget = SelectorActive },
|
||||
},
|
||||
privateChannelFlow = new ChannelListItemFlow("DIRECT MESSAGES"),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public void AddChannel(Channel channel)
|
||||
{
|
||||
if (channelMap.ContainsKey(channel))
|
||||
return;
|
||||
|
||||
ChannelListItem item = new ChannelListItem(channel);
|
||||
item.OnRequestSelect += chan => OnRequestSelect?.Invoke(chan);
|
||||
item.OnRequestLeave += chan => OnRequestLeave?.Invoke(chan);
|
||||
item.SelectorActive.BindTarget = SelectorActive;
|
||||
|
||||
ChannelListItemFlow flow = getFlowForChannel(channel);
|
||||
channelMap.Add(channel, item);
|
||||
flow.Add(item);
|
||||
}
|
||||
|
||||
public void RemoveChannel(Channel channel)
|
||||
{
|
||||
if (!channelMap.ContainsKey(channel))
|
||||
return;
|
||||
|
||||
ChannelListItem item = channelMap[channel];
|
||||
ChannelListItemFlow flow = getFlowForChannel(channel);
|
||||
|
||||
channelMap.Remove(channel);
|
||||
flow.Remove(item);
|
||||
}
|
||||
|
||||
public ChannelListItem GetItem(Channel channel)
|
||||
{
|
||||
if (!channelMap.ContainsKey(channel))
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
return channelMap[channel];
|
||||
}
|
||||
|
||||
private ChannelListItemFlow getFlowForChannel(Channel channel)
|
||||
{
|
||||
switch (channel.Type)
|
||||
{
|
||||
case ChannelType.Public:
|
||||
return publicChannelFlow;
|
||||
|
||||
case ChannelType.PM:
|
||||
return privateChannelFlow;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private class ChannelListItemFlow : FillFlowContainer
|
||||
{
|
||||
public ChannelListItemFlow(string label)
|
||||
{
|
||||
Direction = FillDirection.Vertical;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Add(new OsuSpriteText
|
||||
{
|
||||
Text = label,
|
||||
Margin = new MarginPadding { Left = 18, Bottom = 5 },
|
||||
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -29,12 +29,14 @@ namespace osu.Game.Overlays.Chat.ChannelList
|
||||
|
||||
public readonly BindableBool Unread = new BindableBool();
|
||||
|
||||
public readonly BindableBool SelectorActive = new BindableBool();
|
||||
|
||||
private readonly Channel channel;
|
||||
|
||||
private Box? hoverBox;
|
||||
private Box? selectBox;
|
||||
private OsuSpriteText? text;
|
||||
private ChannelListItemCloseButton? close;
|
||||
private Box hoverBox = null!;
|
||||
private Box selectBox = null!;
|
||||
private OsuSpriteText text = null!;
|
||||
private ChannelListItemCloseButton close = null!;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<Channel> selectedChannel { get; set; } = null!;
|
||||
@ -124,31 +126,26 @@ namespace osu.Game.Overlays.Chat.ChannelList
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
selectedChannel.BindValueChanged(change =>
|
||||
{
|
||||
if (change.NewValue == channel)
|
||||
selectBox?.FadeIn(300, Easing.OutQuint);
|
||||
else
|
||||
selectBox?.FadeOut(200, Easing.OutQuint);
|
||||
}, true);
|
||||
selectedChannel.BindValueChanged(_ => updateSelectState(), true);
|
||||
SelectorActive.BindValueChanged(_ => updateSelectState(), true);
|
||||
|
||||
Unread.BindValueChanged(change =>
|
||||
{
|
||||
text!.FadeColour(change.NewValue ? colourProvider.Content1 : colourProvider.Light3, 300, Easing.OutQuint);
|
||||
text.FadeColour(change.NewValue ? colourProvider.Content1 : colourProvider.Light3, 300, Easing.OutQuint);
|
||||
}, true);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
hoverBox?.FadeIn(300, Easing.OutQuint);
|
||||
close?.FadeIn(300, Easing.OutQuint);
|
||||
hoverBox.FadeIn(300, Easing.OutQuint);
|
||||
close.FadeIn(300, Easing.OutQuint);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
hoverBox?.FadeOut(200, Easing.OutQuint);
|
||||
close?.FadeOut(200, Easing.OutQuint);
|
||||
hoverBox.FadeOut(200, Easing.OutQuint);
|
||||
close.FadeOut(200, Easing.OutQuint);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
@ -167,5 +164,13 @@ namespace osu.Game.Overlays.Chat.ChannelList
|
||||
Masking = true,
|
||||
};
|
||||
}
|
||||
|
||||
private void updateSelectState()
|
||||
{
|
||||
if (selectedChannel.Value == channel && !SelectorActive.Value)
|
||||
selectBox.FadeIn(300, Easing.OutQuint);
|
||||
else
|
||||
selectBox.FadeOut(200, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
91
osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs
Normal file
91
osu.Game/Overlays/Chat/ChannelList/ChannelListSelector.cs
Normal file
@ -0,0 +1,91 @@
|
||||
// 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.
|
||||
|
||||
#nullable enable
|
||||
|
||||
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.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Overlays.Chat.ChannelList
|
||||
{
|
||||
public class ChannelListSelector : OsuClickableContainer
|
||||
{
|
||||
public readonly BindableBool SelectorActive = new BindableBool();
|
||||
|
||||
private Box hoverBox = null!;
|
||||
private Box selectBox = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
Height = 30;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
hoverBox = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background3,
|
||||
Alpha = 0f,
|
||||
},
|
||||
selectBox = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background4,
|
||||
Alpha = 0f,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = 18, Right = 10 },
|
||||
Child = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Text = "Add More Channels",
|
||||
Font = OsuFont.Torus.With(size: 17, weight: FontWeight.SemiBold),
|
||||
Colour = colourProvider.Light3,
|
||||
Margin = new MarginPadding { Bottom = 2 },
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Truncate = true,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
SelectorActive.BindValueChanged(selector =>
|
||||
{
|
||||
if (selector.NewValue)
|
||||
selectBox.FadeIn(300, Easing.OutQuint);
|
||||
else
|
||||
selectBox.FadeOut(200, Easing.OutQuint);
|
||||
}, true);
|
||||
|
||||
Action = () => SelectorActive.Value = true;
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
hoverBox.FadeIn(300, Easing.OutQuint);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
hoverBox.FadeOut(200, Easing.OutQuint);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -160,7 +160,7 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 1,
|
||||
PlaceholderText = "type your message",
|
||||
PlaceholderText = Resources.Localisation.Web.ChatStrings.InputPlaceholder,
|
||||
ReleaseFocusOnCommit = false,
|
||||
HoldFocus = true,
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Comments.Buttons
|
||||
{
|
||||
@ -25,7 +26,7 @@ namespace osu.Game.Overlays.Comments.Buttons
|
||||
{
|
||||
public ButtonContent()
|
||||
{
|
||||
Text = "load replies";
|
||||
Text = CommentsStrings.LoadReplies;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using osu.Game.Graphics.Sprites;
|
||||
using System.Collections.Generic;
|
||||
using osuTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Comments.Buttons
|
||||
{
|
||||
@ -38,7 +39,7 @@ namespace osu.Game.Overlays.Comments.Buttons
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
|
||||
Text = "show more"
|
||||
Text = CommonStrings.ButtonsShowMore
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
@ -54,7 +55,7 @@ namespace osu.Game.Overlays.Comments
|
||||
Origin = Anchor.Centre,
|
||||
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
|
||||
Margin = new MarginPadding { Horizontal = 20 },
|
||||
Text = @"Cancel"
|
||||
Text = CommonStrings.ButtonsCancel
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -16,6 +16,7 @@ using osu.Framework.Threading;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using APIUser = osu.Game.Online.API.Requests.Responses.APIUser;
|
||||
|
||||
namespace osu.Game.Overlays.Comments
|
||||
@ -328,7 +329,7 @@ namespace osu.Game.Overlays.Comments
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Margin = new MarginPadding { Left = 50 },
|
||||
Text = @"No comments yet."
|
||||
Text = CommentsStrings.Empty
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ using osu.Game.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osuTK;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
@ -91,7 +93,7 @@ namespace osu.Game.Overlays.Comments
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
|
||||
Text = @"Show deleted"
|
||||
Text = CommonStrings.ButtonsShowDeleted
|
||||
}
|
||||
},
|
||||
});
|
||||
@ -126,9 +128,13 @@ namespace osu.Game.Overlays.Comments
|
||||
|
||||
public enum CommentsSortCriteria
|
||||
{
|
||||
[System.ComponentModel.Description(@"Recent")]
|
||||
[LocalisableDescription(typeof(SortStrings), nameof(SortStrings.New))]
|
||||
New,
|
||||
|
||||
[LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Old))]
|
||||
Old,
|
||||
|
||||
[LocalisableDescription(typeof(SortStrings), nameof(SortStrings.Top))]
|
||||
Top
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,10 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
@ -18,7 +21,8 @@ namespace osu.Game.Overlays.Comments
|
||||
|
||||
private void onCurrentChanged(ValueChangedEvent<int> count)
|
||||
{
|
||||
Text = $@"Show More ({count.NewValue})".ToUpper();
|
||||
Text = new TranslatableString(@"_", "{0} ({1})",
|
||||
CommonStrings.ButtonsShowMore.ToUpper(), count.NewValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
Alpha = Comment.IsDeleted ? 1 : 0,
|
||||
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
|
||||
Text = "deleted"
|
||||
Text = CommentsStrings.Deleted
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,6 +9,7 @@ using osuTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
@ -39,7 +40,7 @@ namespace osu.Game.Overlays.Comments
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: 20, italics: true),
|
||||
Colour = colourProvider.Light1,
|
||||
Text = @"Comments"
|
||||
Text = CommentsStrings.Title
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
|
@ -14,6 +14,7 @@ using osu.Game.Database;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Users;
|
||||
@ -106,7 +107,7 @@ namespace osu.Game.Overlays.Dashboard
|
||||
public readonly APIUser User;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OsuGame game { get; set; }
|
||||
private IPerformFromScreenRunner performer { get; set; }
|
||||
|
||||
public PlayingUserPanel(APIUser user)
|
||||
{
|
||||
@ -137,10 +138,10 @@ namespace osu.Game.Overlays.Dashboard
|
||||
new PurpleTriangleButton
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = "Watch",
|
||||
Text = "Spectate",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Action = () => game?.PerformFromScreen(s => s.Push(new SoloSpectator(User))),
|
||||
Action = () => performer?.PerformFromScreen(s => s.Push(new SoloSpectator(User))),
|
||||
Enabled = { Value = User.Id != api.LocalUser.Value.Id }
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
@ -49,7 +50,7 @@ namespace osu.Game.Overlays.Dashboard.Home
|
||||
flow.AddRange(beatmapSets.Select(CreateBeatmapPanel));
|
||||
}
|
||||
|
||||
protected abstract string Title { get; }
|
||||
protected abstract LocalisableString Title { get; }
|
||||
|
||||
protected abstract DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet);
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Dashboard.Home
|
||||
{
|
||||
@ -15,6 +17,6 @@ namespace osu.Game.Overlays.Dashboard.Home
|
||||
|
||||
protected override DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet) => new DashboardNewBeatmapPanel(beatmapSet);
|
||||
|
||||
protected override string Title => "New Ranked Beatmaps";
|
||||
protected override LocalisableString Title => HomeStrings.UserBeatmapsNew;
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Dashboard.Home
|
||||
{
|
||||
@ -15,6 +17,6 @@ namespace osu.Game.Overlays.Dashboard.Home
|
||||
|
||||
protected override DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet) => new DashboardPopularBeatmapPanel(beatmapSet);
|
||||
|
||||
protected override string Title => "Popular Beatmaps";
|
||||
protected override LocalisableString Title => HomeStrings.UserBeatmapsPopular;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Dashboard.Home.News
|
||||
@ -35,7 +36,7 @@ namespace osu.Game.Overlays.Dashboard.Home.News
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Margin = new MarginPadding { Vertical = 20 },
|
||||
Text = "see more"
|
||||
Text = CommonStrings.ButtonsSeeMore
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Dialog
|
||||
{
|
||||
@ -33,7 +34,7 @@ namespace osu.Game.Overlays.Dialog
|
||||
},
|
||||
new PopupDialogCancelButton
|
||||
{
|
||||
Text = Localisation.CommonStrings.Cancel,
|
||||
Text = CommonStrings.ButtonsCancel,
|
||||
Action = onCancel
|
||||
},
|
||||
};
|
||||
|
@ -216,7 +216,7 @@ namespace osu.Game.Overlays.Dialog
|
||||
};
|
||||
|
||||
// It's important we start in a visible state so our state fires on hide, even before load.
|
||||
// This is used by the DialogOverlay to know when the dialog was dismissed.
|
||||
// This is used by the dialog overlay to know when the dialog was dismissed.
|
||||
Show();
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ using osu.Game.Audio.Effects;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class DialogOverlay : OsuFocusedOverlayContainer
|
||||
public class DialogOverlay : OsuFocusedOverlayContainer, IDialogOverlay
|
||||
{
|
||||
private readonly Container dialogContainer;
|
||||
|
||||
|
32
osu.Game/Overlays/IDialogOverlay.cs
Normal file
32
osu.Game/Overlays/IDialogOverlay.cs
Normal file
@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
/// <summary>
|
||||
/// A global overlay that can show popup dialogs.
|
||||
/// </summary>
|
||||
[Cached(typeof(IDialogOverlay))]
|
||||
public interface IDialogOverlay
|
||||
{
|
||||
/// <summary>
|
||||
/// Push a new dialog for display.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will immediate dismiss any already displayed dialog (cancelling the action).
|
||||
/// If the dialog instance provided is already displayed, it will be a noop.
|
||||
/// </remarks>
|
||||
/// <param name="dialog">The dialog to be presented.</param>
|
||||
void Push(PopupDialog dialog);
|
||||
|
||||
/// <summary>
|
||||
/// The currently displayed dialog, if any.
|
||||
/// </summary>
|
||||
PopupDialog? CurrentDialog { get; }
|
||||
}
|
||||
}
|
32
osu.Game/Overlays/INotificationOverlay.cs
Normal file
32
osu.Game/Overlays/INotificationOverlay.cs
Normal file
@ -0,0 +1,32 @@
|
||||
// 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.Game.Overlays.Notifications;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
/// <summary>
|
||||
/// An overlay which is capable of showing notifications to the user.
|
||||
/// </summary>
|
||||
[Cached]
|
||||
public interface INotificationOverlay
|
||||
{
|
||||
/// <summary>
|
||||
/// Post a new notification for display.
|
||||
/// </summary>
|
||||
/// <param name="notification">The notification to display.</param>
|
||||
void Post(Notification notification);
|
||||
|
||||
/// <summary>
|
||||
/// Hide the overlay, if it is currently visible.
|
||||
/// </summary>
|
||||
void Hide();
|
||||
|
||||
/// <summary>
|
||||
/// Current number of unread notifications.
|
||||
/// </summary>
|
||||
IBindable<int> UnreadCount { get; }
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Login
|
||||
@ -50,14 +51,14 @@ namespace osu.Game.Overlays.Login
|
||||
{
|
||||
username = new OsuTextBox
|
||||
{
|
||||
PlaceholderText = "username",
|
||||
PlaceholderText = UsersStrings.LoginUsername,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = api?.ProvidedUsername ?? string.Empty,
|
||||
TabbableContentContainer = this
|
||||
},
|
||||
password = new OsuPasswordTextBox
|
||||
{
|
||||
PlaceholderText = "password",
|
||||
PlaceholderText = UsersStrings.LoginPassword,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
@ -88,7 +89,7 @@ namespace osu.Game.Overlays.Login
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = new SettingsButton
|
||||
{
|
||||
Text = "Sign in",
|
||||
Text = UsersStrings.LoginButton,
|
||||
Action = performLogin
|
||||
},
|
||||
}
|
||||
|
@ -2,11 +2,14 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.Login
|
||||
{
|
||||
public enum UserAction
|
||||
{
|
||||
[LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusOnline))]
|
||||
Online,
|
||||
|
||||
[Description(@"Do not disturb")]
|
||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Mods
|
||||
AutoSizeAxes = Axes.X,
|
||||
Masking = true,
|
||||
CornerRadius = ModPanel.CORNER_RADIUS,
|
||||
Shear = new Vector2(ModPanel.SHEAR_X, 0),
|
||||
Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
underlayBackground = new Box
|
||||
@ -98,7 +98,7 @@ namespace osu.Game.Overlays.Mods
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Margin = new MarginPadding { Horizontal = 18 },
|
||||
Shear = new Vector2(-ModPanel.SHEAR_X, 0),
|
||||
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0),
|
||||
Text = "Difficulty Multiplier",
|
||||
Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold)
|
||||
}
|
||||
@ -109,7 +109,7 @@ namespace osu.Game.Overlays.Mods
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Shear = new Vector2(-ModPanel.SHEAR_X, 0),
|
||||
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0),
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(2, 0),
|
||||
Children = new Drawable[]
|
||||
|
@ -37,7 +37,9 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
private void updateIncompatibility()
|
||||
{
|
||||
incompatible.Value = selectedMods.Value.Count > 0 && !selectedMods.Value.Contains(Mod) && !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(Mod));
|
||||
incompatible.Value = selectedMods.Value.Count > 0
|
||||
&& selectedMods.Value.All(selected => selected.GetType() != Mod.GetType())
|
||||
&& !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(Mod));
|
||||
}
|
||||
|
||||
protected override void UpdateState()
|
||||
@ -46,8 +48,8 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
if (incompatible.Value)
|
||||
{
|
||||
Colour4 backgroundColour = ColourProvider.Background5;
|
||||
Colour4 textBackgroundColour = ColourProvider.Background4;
|
||||
Colour4 backgroundColour = ColourProvider.Background6;
|
||||
Colour4 textBackgroundColour = ColourProvider.Background5;
|
||||
|
||||
Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(backgroundColour, textBackgroundColour), TRANSITION_DURATION, Easing.OutQuint);
|
||||
Background.FadeColour(backgroundColour, TRANSITION_DURATION, Easing.OutQuint);
|
||||
|
@ -54,6 +54,8 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public Bindable<IReadOnlyList<Mod>> SelectedMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
|
||||
protected virtual ModPanel CreateModPanel(Mod mod) => new ModPanel(mod);
|
||||
|
||||
private readonly Key[]? toggleKeys;
|
||||
|
||||
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> availableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
||||
@ -79,7 +81,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
Width = 320;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Shear = new Vector2(ModPanel.SHEAR_X, 0);
|
||||
Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0);
|
||||
|
||||
Container controlContainer;
|
||||
InternalChildren = new Drawable[]
|
||||
@ -113,7 +115,7 @@ namespace osu.Game.Overlays.Mods
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Shear = new Vector2(-ModPanel.SHEAR_X, 0),
|
||||
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0),
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = 17,
|
||||
@ -193,7 +195,7 @@ namespace osu.Game.Overlays.Mods
|
||||
Scale = new Vector2(0.8f),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
LabelText = "Enable All",
|
||||
Shear = new Vector2(-ModPanel.SHEAR_X, 0)
|
||||
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)
|
||||
});
|
||||
panelFlow.Padding = new MarginPadding
|
||||
{
|
||||
@ -258,10 +260,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
cancellationTokenSource?.Cancel();
|
||||
|
||||
var panels = newMods.Select(mod => new ModPanel(mod)
|
||||
{
|
||||
Shear = new Vector2(-ModPanel.SHEAR_X, 0)
|
||||
});
|
||||
var panels = newMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)));
|
||||
|
||||
Task? loadTask;
|
||||
|
||||
|
@ -42,7 +42,6 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
protected const double TRANSITION_DURATION = 150;
|
||||
|
||||
public const float SHEAR_X = 0.2f;
|
||||
public const float CORNER_RADIUS = 7;
|
||||
|
||||
protected const float HEIGHT = 42;
|
||||
@ -67,7 +66,7 @@ namespace osu.Game.Overlays.Mods
|
||||
Content.Masking = true;
|
||||
Content.CornerRadius = CORNER_RADIUS;
|
||||
Content.BorderThickness = 2;
|
||||
Content.Shear = new Vector2(SHEAR_X, 0);
|
||||
Content.Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -83,7 +82,7 @@ namespace osu.Game.Overlays.Mods
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Active = { BindTarget = Active },
|
||||
Shear = new Vector2(-SHEAR_X, 0),
|
||||
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0),
|
||||
Scale = new Vector2(HEIGHT / ModSwitchSmall.DEFAULT_SIZE)
|
||||
}
|
||||
},
|
||||
@ -116,10 +115,10 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
Text = mod.Name,
|
||||
Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold),
|
||||
Shear = new Vector2(-SHEAR_X, 0),
|
||||
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0),
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Left = -18 * SHEAR_X
|
||||
Left = -18 * ShearedOverlayContainer.SHEAR
|
||||
}
|
||||
},
|
||||
new OsuSpriteText
|
||||
@ -128,7 +127,7 @@ namespace osu.Game.Overlays.Mods
|
||||
Font = OsuFont.Default.With(size: 12),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Truncate = true,
|
||||
Shear = new Vector2(-SHEAR_X, 0)
|
||||
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -159,7 +158,7 @@ namespace osu.Game.Overlays.Mods
|
||||
playStateChangeSamples();
|
||||
UpdateState();
|
||||
});
|
||||
Filtered.BindValueChanged(_ => updateFilterState());
|
||||
Filtered.BindValueChanged(_ => updateFilterState(), true);
|
||||
|
||||
UpdateState();
|
||||
FinishTransforms(true);
|
||||
|
@ -20,6 +20,7 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Utils;
|
||||
@ -317,7 +318,7 @@ namespace osu.Game.Overlays.Mods
|
||||
CloseButton = new TriangleButton
|
||||
{
|
||||
Width = 180,
|
||||
Text = "Close",
|
||||
Text = CommonStrings.ButtonsClose,
|
||||
Action = Hide,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
|
@ -1,6 +1,8 @@
|
||||
// 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.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@ -9,7 +11,6 @@ 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.Framework.Layout;
|
||||
using osu.Game.Configuration;
|
||||
@ -21,158 +22,58 @@ using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
public class ModSelectScreen : OsuFocusedOverlayContainer
|
||||
public abstract class ModSelectScreen : ShearedOverlayContainer
|
||||
{
|
||||
[Cached]
|
||||
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||
protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Green;
|
||||
|
||||
[Cached]
|
||||
public Bindable<IReadOnlyList<Mod>> SelectedMods { get; private set; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
|
||||
protected override bool StartHidden => true;
|
||||
private Func<Mod, bool> isValidMod = m => true;
|
||||
|
||||
public Func<Mod, bool> IsValidMod
|
||||
{
|
||||
get => isValidMod;
|
||||
set
|
||||
{
|
||||
isValidMod = value ?? throw new ArgumentNullException(nameof(value));
|
||||
|
||||
if (IsLoaded)
|
||||
updateAvailableMods();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether configurable <see cref="Mod"/>s can be configured by the local user.
|
||||
/// </summary>
|
||||
protected virtual bool AllowCustomisation => true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the total score multiplier calculated from the current selected set of mods should be shown.
|
||||
/// </summary>
|
||||
protected virtual bool ShowTotalMultiplier => true;
|
||||
|
||||
protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys);
|
||||
|
||||
private readonly BindableBool customisationVisible = new BindableBool();
|
||||
|
||||
private DifficultyMultiplierDisplay multiplierDisplay;
|
||||
private ModSettingsArea modSettingsArea;
|
||||
private FillFlowContainer<ModColumn> columnFlow;
|
||||
private GridContainer grid;
|
||||
private Container mainContent;
|
||||
|
||||
private PopupScreenTitle header;
|
||||
private Container footer;
|
||||
private DifficultyMultiplierDisplay? multiplierDisplay;
|
||||
private ModSettingsArea modSettingsArea = null!;
|
||||
private FillFlowContainer<ModColumn> columnFlow = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
RelativePositionAxes = Axes.Both;
|
||||
Header.Title = "Mod Select";
|
||||
Header.Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.";
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
mainContent = new Container
|
||||
new ClickToReturnContainer
|
||||
{
|
||||
Origin = Anchor.BottomCentre,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
grid = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.Absolute, 75),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
header = new PopupScreenTitle
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Title = "Mod Select",
|
||||
Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.",
|
||||
Close = Hide
|
||||
}
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.X,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = 0.3f,
|
||||
Height = DifficultyMultiplierDisplay.HEIGHT,
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Horizontal = 100,
|
||||
Vertical = 10
|
||||
},
|
||||
Child = multiplierDisplay = new DifficultyMultiplierDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
}
|
||||
}
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Depth = float.MaxValue,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuScrollContainer(Direction.Horizontal)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = false,
|
||||
ClampExtension = 100,
|
||||
ScrollbarOverlapsContent = false,
|
||||
Child = columnFlow = new ModColumnContainer
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Margin = new MarginPadding { Right = 70 },
|
||||
Children = new[]
|
||||
{
|
||||
new ModColumn(ModType.DifficultyReduction, false, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }),
|
||||
new ModColumn(ModType.DifficultyIncrease, false, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }),
|
||||
new ModColumn(ModType.Automation, false, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }),
|
||||
new ModColumn(ModType.Conversion, false),
|
||||
new ModColumn(ModType.Fun, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new[] { Empty() }
|
||||
}
|
||||
},
|
||||
footer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 50,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Colour = colourProvider.Background5
|
||||
},
|
||||
new ShearedToggleButton(200)
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Vertical = 14, Left = 70 },
|
||||
Text = "Mod Customisation",
|
||||
Active = { BindTarget = customisationVisible }
|
||||
}
|
||||
}
|
||||
},
|
||||
new ClickToReturnContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
HandleMouse = { BindTarget = customisationVisible },
|
||||
OnClicked = () => customisationVisible.Value = false
|
||||
}
|
||||
}
|
||||
HandleMouse = { BindTarget = customisationVisible },
|
||||
OnClicked = () => customisationVisible.Value = false
|
||||
},
|
||||
modSettingsArea = new ModSettingsArea
|
||||
{
|
||||
@ -180,9 +81,76 @@ namespace osu.Game.Overlays.Mods
|
||||
Origin = Anchor.BottomCentre,
|
||||
Height = 0
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
columnFlow.Shear = new Vector2(ModPanel.SHEAR_X, 0);
|
||||
MainAreaContent.AddRange(new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Top = (ShowTotalMultiplier ? DifficultyMultiplierDisplay.HEIGHT : 0) + PADDING,
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuScrollContainer(Direction.Horizontal)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = false,
|
||||
ClampExtension = 100,
|
||||
ScrollbarOverlapsContent = false,
|
||||
Child = columnFlow = new ModColumnContainer
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
Shear = new Vector2(SHEAR, 0),
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Margin = new MarginPadding { Right = 70 },
|
||||
Children = new[]
|
||||
{
|
||||
CreateModColumn(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }),
|
||||
CreateModColumn(ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }),
|
||||
CreateModColumn(ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }),
|
||||
CreateModColumn(ModType.Conversion),
|
||||
CreateModColumn(ModType.Fun)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (ShowTotalMultiplier)
|
||||
{
|
||||
MainAreaContent.Add(new Container
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = DifficultyMultiplierDisplay.HEIGHT,
|
||||
Margin = new MarginPadding { Horizontal = 100 },
|
||||
Child = multiplierDisplay = new DifficultyMultiplierDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (AllowCustomisation)
|
||||
{
|
||||
Footer.Add(new ShearedToggleButton(200)
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Vertical = PADDING, Left = 70 },
|
||||
Text = "Mod Customisation",
|
||||
Active = { BindTarget = customisationVisible }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -204,10 +172,15 @@ namespace osu.Game.Overlays.Mods
|
||||
}
|
||||
|
||||
customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true);
|
||||
|
||||
updateAvailableMods();
|
||||
}
|
||||
|
||||
private void updateMultiplier()
|
||||
{
|
||||
if (multiplierDisplay == null)
|
||||
return;
|
||||
|
||||
double multiplier = 1.0;
|
||||
|
||||
foreach (var mod in SelectedMods.Value)
|
||||
@ -216,8 +189,17 @@ namespace osu.Game.Overlays.Mods
|
||||
multiplierDisplay.Current.Value = multiplier;
|
||||
}
|
||||
|
||||
private void updateAvailableMods()
|
||||
{
|
||||
foreach (var column in columnFlow)
|
||||
column.Filter = isValidMod;
|
||||
}
|
||||
|
||||
private void updateCustomisation(ValueChangedEvent<IReadOnlyList<Mod>> valueChangedEvent)
|
||||
{
|
||||
if (!AllowCustomisation)
|
||||
return;
|
||||
|
||||
bool anyCustomisableMod = false;
|
||||
bool anyModWithRequiredCustomisationAdded = false;
|
||||
|
||||
@ -247,12 +229,12 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
const double transition_duration = 300;
|
||||
|
||||
grid.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic);
|
||||
MainAreaContent.FadeColour(customisationVisible.Value ? Colour4.Gray : Colour4.White, transition_duration, Easing.InOutCubic);
|
||||
|
||||
float modAreaHeight = customisationVisible.Value ? ModSettingsArea.HEIGHT : 0;
|
||||
|
||||
modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic);
|
||||
mainContent.TransformTo(nameof(Margin), new MarginPadding { Bottom = modAreaHeight }, transition_duration, Easing.InOutCubic);
|
||||
TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic);
|
||||
}
|
||||
|
||||
private bool selectionBindableSyncInProgress;
|
||||
@ -287,12 +269,8 @@ namespace osu.Game.Overlays.Mods
|
||||
const double fade_in_duration = 400;
|
||||
|
||||
base.PopIn();
|
||||
this.FadeIn(fade_in_duration, Easing.OutQuint);
|
||||
|
||||
header.MoveToY(0, fade_in_duration, Easing.OutQuint);
|
||||
footer.MoveToY(0, fade_in_duration, Easing.OutQuint);
|
||||
|
||||
multiplierDisplay
|
||||
multiplierDisplay?
|
||||
.Delay(fade_in_duration * 0.65f)
|
||||
.FadeIn(fade_in_duration / 2, Easing.OutQuint)
|
||||
.ScaleTo(1, fade_in_duration, Easing.OutElastic);
|
||||
@ -311,15 +289,11 @@ namespace osu.Game.Overlays.Mods
|
||||
const double fade_out_duration = 500;
|
||||
|
||||
base.PopOut();
|
||||
this.FadeOut(fade_out_duration, Easing.OutQuint);
|
||||
|
||||
multiplierDisplay
|
||||
multiplierDisplay?
|
||||
.FadeOut(fade_out_duration / 2, Easing.OutQuint)
|
||||
.ScaleTo(0.75f, fade_out_duration, Easing.OutQuint);
|
||||
|
||||
header.MoveToY(-header.DrawHeight, fade_out_duration, Easing.OutQuint);
|
||||
footer.MoveToY(footer.DrawHeight, fade_out_duration, Easing.OutQuint);
|
||||
|
||||
for (int i = 0; i < columnFlow.Count; i++)
|
||||
{
|
||||
const float distance = 700;
|
||||
@ -355,7 +329,7 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Left = DrawHeight * ModPanel.SHEAR_X,
|
||||
Left = DrawHeight * SHEAR,
|
||||
Bottom = 10
|
||||
};
|
||||
|
||||
@ -368,7 +342,7 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
public BindableBool HandleMouse { get; } = new BindableBool();
|
||||
|
||||
public Action OnClicked { get; set; }
|
||||
public Action? OnClicked { get; set; }
|
||||
|
||||
protected override bool Handle(UIEvent e)
|
||||
{
|
||||
|
138
osu.Game/Overlays/Mods/ShearedOverlayContainer.cs
Normal file
138
osu.Game/Overlays/Mods/ShearedOverlayContainer.cs
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
/// <summary>
|
||||
/// A sheared overlay which provides a header and footer and basic animations.
|
||||
/// Exposes <see cref="TopLevelContent"/>, <see cref="MainAreaContent"/> and <see cref="Footer"/> as valid targets for content.
|
||||
/// </summary>
|
||||
public abstract class ShearedOverlayContainer : OsuFocusedOverlayContainer
|
||||
{
|
||||
protected const float PADDING = 14;
|
||||
|
||||
public const float SHEAR = 0.2f;
|
||||
|
||||
[Cached]
|
||||
protected readonly OverlayColourProvider ColourProvider;
|
||||
|
||||
/// <summary>
|
||||
/// The overlay's header.
|
||||
/// </summary>
|
||||
protected ShearedOverlayHeader Header { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The overlay's footer.
|
||||
/// </summary>
|
||||
protected Container Footer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A container containing all content, including the header and footer.
|
||||
/// May be used for overlay-wide animations.
|
||||
/// </summary>
|
||||
protected Container TopLevelContent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A container for content that is to be displayed between the header and footer.
|
||||
/// </summary>
|
||||
protected Container MainAreaContent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A container for content that is to be displayed inside the footer.
|
||||
/// </summary>
|
||||
protected Container FooterContent { get; private set; }
|
||||
|
||||
protected abstract OverlayColourScheme ColourScheme { get; }
|
||||
|
||||
protected override bool StartHidden => true;
|
||||
|
||||
protected override bool BlockNonPositionalInput => true;
|
||||
|
||||
protected ShearedOverlayContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
ColourProvider = new OverlayColourProvider(ColourScheme);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
const float footer_height = 50;
|
||||
|
||||
Child = TopLevelContent = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Header = new ShearedOverlayHeader
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Depth = float.MinValue,
|
||||
Origin = Anchor.TopCentre,
|
||||
Close = Hide
|
||||
},
|
||||
MainAreaContent = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Top = ShearedOverlayHeader.HEIGHT,
|
||||
Bottom = footer_height + PADDING,
|
||||
}
|
||||
},
|
||||
Footer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Depth = float.MinValue,
|
||||
Height = footer_height,
|
||||
Margin = new MarginPadding { Top = PADDING },
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourProvider.Background5
|
||||
},
|
||||
FooterContent = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
const double fade_in_duration = 400;
|
||||
|
||||
base.PopIn();
|
||||
this.FadeIn(fade_in_duration, Easing.OutQuint);
|
||||
|
||||
Header.MoveToY(0, fade_in_duration, Easing.OutQuint);
|
||||
Footer.MoveToY(0, fade_in_duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
const double fade_out_duration = 500;
|
||||
|
||||
base.PopOut();
|
||||
this.FadeOut(fade_out_duration, Easing.OutQuint);
|
||||
|
||||
Header.MoveToY(-Header.DrawHeight, fade_out_duration, Easing.OutQuint);
|
||||
Footer.MoveToY(Footer.DrawHeight, fade_out_duration, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
24
osu.Game/Overlays/Mods/UserModSelectScreen.cs
Normal file
24
osu.Game/Overlays/Mods/UserModSelectScreen.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 JetBrains.Annotations;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
public class UserModSelectScreen : ModSelectScreen
|
||||
{
|
||||
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys);
|
||||
|
||||
private class UserModColumn : ModColumn
|
||||
{
|
||||
public UserModColumn(ModType modType, bool allowBulkSelection, [CanBeNull] Key[] toggleKeys = null)
|
||||
: base(modType, allowBulkSelection, toggleKeys)
|
||||
{
|
||||
}
|
||||
|
||||
protected override ModPanel CreateModPanel(Mod mod) => new IncompatibilityDisplayingModPanel(mod);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,11 +15,12 @@ using osu.Framework.Localisation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using NotificationsStrings = osu.Game.Localisation.NotificationsStrings;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent
|
||||
public class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent, INotificationOverlay
|
||||
{
|
||||
public string IconTexture => "Icons/Hexacons/notification";
|
||||
public LocalisableString Title => NotificationsStrings.HeaderTitle;
|
||||
@ -61,7 +62,7 @@ namespace osu.Game.Overlays
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Children = new[]
|
||||
{
|
||||
new NotificationSection(@"Notifications", @"Clear All")
|
||||
new NotificationSection(AccountsStrings.NotificationsTitle, "Clear All")
|
||||
{
|
||||
AcceptTypes = new[] { typeof(SimpleNotification) }
|
||||
},
|
||||
@ -99,7 +100,9 @@ namespace osu.Game.Overlays
|
||||
OverlayActivationMode.BindValueChanged(_ => updateProcessingMode(), true);
|
||||
}
|
||||
|
||||
public readonly BindableInt UnreadCount = new BindableInt();
|
||||
public IBindable<int> UnreadCount => unreadCount;
|
||||
|
||||
private readonly BindableInt unreadCount = new BindableInt();
|
||||
|
||||
private int runningDepth;
|
||||
|
||||
@ -111,10 +114,6 @@ namespace osu.Game.Overlays
|
||||
|
||||
private double? lastSamplePlayback;
|
||||
|
||||
/// <summary>
|
||||
/// Post a new notification for display.
|
||||
/// </summary>
|
||||
/// <param name="notification">The notification to display.</param>
|
||||
public void Post(Notification notification) => postScheduler.Add(() =>
|
||||
{
|
||||
++runningDepth;
|
||||
@ -184,7 +183,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
private void updateCounts()
|
||||
{
|
||||
UnreadCount.Value = sections.Select(c => c.UnreadCount).Sum();
|
||||
unreadCount.Value = sections.Select(c => c.UnreadCount).Sum();
|
||||
}
|
||||
|
||||
private void markAllRead()
|
||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Localisation;
|
||||
@ -34,9 +35,9 @@ namespace osu.Game.Overlays.Notifications
|
||||
|
||||
private readonly string clearButtonText;
|
||||
|
||||
private readonly string titleText;
|
||||
private readonly LocalisableString titleText;
|
||||
|
||||
public NotificationSection(string title, string clearButtonText)
|
||||
public NotificationSection(LocalisableString title, string clearButtonText)
|
||||
{
|
||||
this.clearButtonText = clearButtonText.ToUpperInvariant();
|
||||
titleText = title;
|
||||
@ -84,7 +85,7 @@ namespace osu.Game.Overlays.Notifications
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = titleText.ToUpperInvariant(),
|
||||
Text = titleText.ToUpper(),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold)
|
||||
},
|
||||
countDrawable = new OsuSpriteText
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Humanizer;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -11,10 +10,12 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -83,7 +84,7 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
if (user == null) return;
|
||||
|
||||
if (user.JoinDate.ToUniversalTime().Year < 2008)
|
||||
topLinkContainer.AddText("Here since the beginning");
|
||||
topLinkContainer.AddText(UsersStrings.ShowFirstMembers);
|
||||
else
|
||||
{
|
||||
topLinkContainer.AddText("Joined ");
|
||||
@ -94,7 +95,7 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
|
||||
if (user.IsOnline)
|
||||
{
|
||||
topLinkContainer.AddText("Currently online");
|
||||
topLinkContainer.AddText(UsersStrings.ShowLastvisitOnline);
|
||||
addSpacer(topLinkContainer);
|
||||
}
|
||||
else if (user.LastVisit.HasValue)
|
||||
@ -108,7 +109,16 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
if (user.PlayStyles?.Length > 0)
|
||||
{
|
||||
topLinkContainer.AddText("Plays with ");
|
||||
topLinkContainer.AddText(string.Join(", ", user.PlayStyles.Select(style => style.GetDescription())), embolden);
|
||||
|
||||
LocalisableString playStylesString = user.PlayStyles[0].GetLocalisableDescription();
|
||||
|
||||
for (int i = 1; i < user.PlayStyles.Length; i++)
|
||||
{
|
||||
playStylesString = new TranslatableString(@"_", @"{0}{1}", playStylesString, CommonStrings.ArrayAndWordsConnector);
|
||||
playStylesString = new TranslatableString(@"_", @"{0}{1}", playStylesString, user.PlayStyles[i].GetLocalisableDescription());
|
||||
}
|
||||
|
||||
topLinkContainer.AddText(playStylesString, embolden);
|
||||
|
||||
addSpacer(topLinkContainer);
|
||||
}
|
||||
|
@ -20,11 +20,12 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
|
||||
private const float panel_padding = 10f;
|
||||
private readonly BeatmapSetType type;
|
||||
|
||||
protected override int InitialItemsCount => type == BeatmapSetType.Graveyard ? 2 : 6;
|
||||
|
||||
public PaginatedBeatmapContainer(BeatmapSetType type, Bindable<APIUser> user, LocalisableString headerText)
|
||||
: base(user, headerText)
|
||||
{
|
||||
this.type = type;
|
||||
ItemsPerPage = 6;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -57,8 +58,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
protected override APIRequest<List<APIBeatmapSet>> CreateRequest() =>
|
||||
new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage);
|
||||
protected override APIRequest<List<APIBeatmapSet>> CreateRequest(PaginationParameters pagination) =>
|
||||
new GetUserBeatmapsRequest(User.Value.Id, type, pagination);
|
||||
|
||||
protected override Drawable CreateDrawableItem(APIBeatmapSet model) => model.OnlineID > 0
|
||||
? new BeatmapCardNormal(model)
|
||||
|
@ -19,7 +19,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
|
||||
public PaginatedMostPlayedBeatmapContainer(Bindable<APIUser> user)
|
||||
: base(user, UsersStrings.ShowExtraHistoricalMostPlayedTitle)
|
||||
{
|
||||
ItemsPerPage = 5;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -30,8 +29,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
|
||||
|
||||
protected override int GetCount(APIUser user) => user.BeatmapPlayCountsCount;
|
||||
|
||||
protected override APIRequest<List<APIUserMostPlayedBeatmap>> CreateRequest() =>
|
||||
new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage);
|
||||
protected override APIRequest<List<APIUserMostPlayedBeatmap>> CreateRequest(PaginationParameters pagination) =>
|
||||
new GetUserMostPlayedBeatmapsRequest(User.Value.Id, pagination);
|
||||
|
||||
protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap mostPlayed) =>
|
||||
new DrawableMostPlayedBeatmap(mostPlayed);
|
||||
|
@ -17,11 +17,10 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
|
||||
public PaginatedKudosuHistoryContainer(Bindable<APIUser> user)
|
||||
: base(user, missingText: UsersStrings.ShowExtraKudosuEntryEmpty)
|
||||
{
|
||||
ItemsPerPage = 5;
|
||||
}
|
||||
|
||||
protected override APIRequest<List<APIKudosuHistory>> CreateRequest()
|
||||
=> new GetUserKudosuHistoryRequest(User.Value.Id, VisiblePages++, ItemsPerPage);
|
||||
protected override APIRequest<List<APIKudosuHistory>> CreateRequest(PaginationParameters pagination)
|
||||
=> new GetUserKudosuHistoryRequest(User.Value.Id, pagination);
|
||||
|
||||
protected override Drawable CreateDrawableItem(APIKudosuHistory item) => new DrawableKudosuHistoryItem(item);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user