Merge branch 'master' into bindable-list-events
30
README.md
@ -13,25 +13,17 @@ Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the
|
||||
|
||||
## Status
|
||||
|
||||
This project is still heavily under development, but is in a state where users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve over the coming months and hopefully bring some new unique features to the table.
|
||||
This project is under heavy development, but is in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
|
||||
|
||||
We are accepting bug reports (please report with as much detail as possible). Feature requests are welcome as long as you read and understand the contribution guidelines listed below.
|
||||
We are accepting bug reports (please report with as much detail as possible). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
|
||||
|
||||
Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh/home/changelog).
|
||||
|
||||
## Requirements
|
||||
|
||||
- A desktop platform with the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) or higher installed.
|
||||
- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding.
|
||||
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore31&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
|
||||
- When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
|
||||
- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
|
||||
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
|
||||
- You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management).
|
||||
- Read peppy's [latest blog post](https://blog.ppy.sh/a-definitive-lazer-faq/) exploring where lazer is currently and the roadmap going forward.
|
||||
|
||||
## Running osu!
|
||||
|
||||
### Releases
|
||||
|
||||
If you are not interested in developing the game, you can still consume our [binary releases](https://github.com/ppy/osu/releases).
|
||||
If you are looking to install or test osu! without setting up a development environment, you can consume our [binary releases](https://github.com/ppy/osu/releases). Handy links below will download the latest version for your operating system of choice:
|
||||
|
||||
**Latest build:**
|
||||
|
||||
@ -39,9 +31,19 @@ If you are not interested in developing the game, you can still consume our [bin
|
||||
| ------------- | ------------- | ------------- | ------------- |
|
||||
|
||||
- **Linux** users are recommended to self-compile until we have official deployment in place.
|
||||
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/dependencies?tabs=netcore31&pivots=os-windows)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
|
||||
|
||||
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
|
||||
|
||||
## Developing or debugging
|
||||
|
||||
Please make sure you have the following prerequisites:
|
||||
|
||||
- A desktop platform with the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) or higher installed.
|
||||
- When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
|
||||
- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
|
||||
- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding.
|
||||
|
||||
### Downloading the source code
|
||||
|
||||
Clone the repository:
|
||||
|
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 4.9 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-apple.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-drop.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 7.0 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-pear.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/old-skin/fruit-plate.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 11 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit0.png
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit100.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit300.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/old-skin/hit50.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-apple-overlay.png
Executable file
After Width: | Height: | Size: 4.6 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-apple.png
Executable file
After Width: | Height: | Size: 4.6 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-bananas-overlay.png
Executable file
After Width: | Height: | Size: 4.8 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-bananas.png
Executable file
After Width: | Height: | Size: 4.8 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-fail.png
Executable file
After Width: | Height: | Size: 62 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-idle.png
Executable file
After Width: | Height: | Size: 130 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-catcher-kiai.png
Executable file
After Width: | Height: | Size: 36 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-drop-overlay.png
Executable file
After Width: | Height: | Size: 4.6 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-drop.png
Executable file
After Width: | Height: | Size: 4.6 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-grapes-overlay.png
Executable file
After Width: | Height: | Size: 4.6 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-grapes.png
Executable file
After Width: | Height: | Size: 4.5 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-orange-overlay.png
Executable file
After Width: | Height: | Size: 4.5 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-orange.png
Executable file
After Width: | Height: | Size: 4.5 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-pear-overlay.png
Executable file
After Width: | Height: | Size: 4.5 KiB |
BIN
osu.Game.Rulesets.Catch.Tests/Resources/special-skin/fruit-pear.png
Executable file
After Width: | Height: | Size: 4.5 KiB |
@ -3,104 +3,31 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneCatcher : OsuTestScene
|
||||
public class TestSceneCatcher : SkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(CatcherSprite),
|
||||
typeof(CatcherArea),
|
||||
};
|
||||
|
||||
private readonly Container container;
|
||||
|
||||
public TestSceneCatcher()
|
||||
{
|
||||
Child = container = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddStep("show default catcher implementation", () => { container.Child = new CatcherSprite(); });
|
||||
|
||||
AddStep("show custom catcher implementation", () =>
|
||||
SetContents(() => new CatcherArea.Catcher
|
||||
{
|
||||
container.Child = new CatchCustomSkinSourceContainer
|
||||
{
|
||||
Child = new CatcherSprite()
|
||||
};
|
||||
RelativePositionAxes = Axes.None,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
}
|
||||
|
||||
private class CatcherCustomSkin : Container
|
||||
{
|
||||
public CatcherCustomSkin()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Blue
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "custom"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Cached(typeof(ISkinSource))]
|
||||
private class CatchCustomSkinSourceContainer : Container, ISkinSource
|
||||
{
|
||||
public event Action SourceChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component)
|
||||
{
|
||||
switch (component.LookupName)
|
||||
{
|
||||
case "Gameplay/catch/fruit-catcher-idle":
|
||||
return new CatcherCustomSkin();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public Texture GetTexture(string componentName) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
@ -13,10 +15,9 @@ using osu.Game.Tests.Visual;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneCatcherArea : OsuTestScene
|
||||
public class TestSceneCatcherArea : SkinnableTestScene
|
||||
{
|
||||
private RulesetInfo catchRuleset;
|
||||
private TestCatcherArea catcherArea;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
@ -26,20 +27,22 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
public TestSceneCatcherArea()
|
||||
{
|
||||
AddSliderStep<float>("CircleSize", 0, 8, 5, createCatcher);
|
||||
AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t));
|
||||
AddToggleStep("Hyperdash", t =>
|
||||
CreatedDrawables.OfType<CatchInputManager>().Select(i => i.Child)
|
||||
.OfType<TestCatcherArea>().ForEach(c => c.ToggleHyperDash(t)));
|
||||
}
|
||||
|
||||
private void createCatcher(float size)
|
||||
{
|
||||
Child = new CatchInputManager(catchRuleset)
|
||||
SetContents(() => new CatchInputManager(catchRuleset)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
|
||||
Child = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.TopLeft
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -5,7 +5,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||
@ -15,68 +14,58 @@ using osuTK;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneFruitObjects : OsuTestScene
|
||||
public class TestSceneFruitObjects : SkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(CatchHitObject),
|
||||
typeof(Fruit),
|
||||
typeof(Droplet),
|
||||
typeof(Banana),
|
||||
typeof(BananaShower),
|
||||
typeof(DrawableCatchHitObject),
|
||||
typeof(DrawableFruit),
|
||||
typeof(DrawableDroplet),
|
||||
typeof(BananaShower),
|
||||
typeof(DrawableBanana),
|
||||
typeof(DrawableBananaShower),
|
||||
typeof(Pulp),
|
||||
};
|
||||
|
||||
public TestSceneFruitObjects()
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
Add(new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
createDrawable(0),
|
||||
createDrawable(1),
|
||||
createDrawable(2),
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
createDrawable(3),
|
||||
createDrawable(4),
|
||||
createDrawable(5),
|
||||
},
|
||||
}
|
||||
});
|
||||
base.LoadComplete();
|
||||
|
||||
foreach (FruitVisualRepresentation rep in Enum.GetValues(typeof(FruitVisualRepresentation)))
|
||||
AddStep($"show {rep}", () => SetContents(() => createDrawable(rep)));
|
||||
}
|
||||
|
||||
private DrawableFruit createDrawable(int index)
|
||||
private DrawableFruit createDrawable(FruitVisualRepresentation rep)
|
||||
{
|
||||
Fruit fruit = index == 5
|
||||
? new Banana
|
||||
{
|
||||
StartTime = 1000000000000,
|
||||
IndexInBeatmap = index,
|
||||
Scale = 1.5f,
|
||||
}
|
||||
: new Fruit
|
||||
{
|
||||
StartTime = 1000000000000,
|
||||
IndexInBeatmap = index,
|
||||
Scale = 1.5f,
|
||||
};
|
||||
Fruit fruit = new TestCatchFruit(rep)
|
||||
{
|
||||
StartTime = 1000000000000,
|
||||
Scale = 1.5f,
|
||||
};
|
||||
|
||||
return new DrawableFruit(fruit)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.None,
|
||||
Position = Vector2.Zero,
|
||||
Alpha = 1,
|
||||
LifetimeStart = double.NegativeInfinity,
|
||||
LifetimeEnd = double.PositiveInfinity,
|
||||
};
|
||||
}
|
||||
|
||||
private class TestCatchFruit : Fruit
|
||||
{
|
||||
public TestCatchFruit(FruitVisualRepresentation rep)
|
||||
{
|
||||
VisualRepresentation = rep;
|
||||
}
|
||||
|
||||
public override FruitVisualRepresentation VisualRepresentation { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
if (result.IsHit && fruit.CanBePlated)
|
||||
{
|
||||
// create a new (cloned) fruit to stay on the plate. the original is faded out immediately.
|
||||
var caughtFruit = (DrawableCatchHitObject)CreateDrawableRepresentation?.Invoke(fruit.HitObject);
|
||||
|
||||
if (caughtFruit == null) return;
|
||||
@ -133,7 +134,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
X = 0.5f;
|
||||
|
||||
Origin = Anchor.TopCentre;
|
||||
Anchor = Anchor.TopLeft;
|
||||
|
||||
Size = new Vector2(CATCHER_SIZE);
|
||||
if (difficulty != null)
|
||||
@ -388,32 +388,24 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePosition(float position)
|
||||
{
|
||||
position = Math.Clamp(position, 0, 1);
|
||||
|
||||
if (position == X)
|
||||
return;
|
||||
|
||||
Scale = new Vector2(Math.Abs(Scale.X) * (position > X ? 1 : -1), Scale.Y);
|
||||
X = position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drop any fruit off the plate.
|
||||
/// </summary>
|
||||
public void Drop()
|
||||
{
|
||||
var fruit = caughtFruit.ToArray();
|
||||
|
||||
foreach (var f in fruit)
|
||||
{
|
||||
if (ExplodingFruitTarget != null)
|
||||
{
|
||||
f.Anchor = Anchor.TopLeft;
|
||||
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
||||
|
||||
caughtFruit.Remove(f);
|
||||
|
||||
ExplodingFruitTarget.Add(f);
|
||||
}
|
||||
|
||||
f.MoveToY(f.Y + 75, 750, Easing.InSine);
|
||||
f.FadeOut(750);
|
||||
|
||||
// todo: this shouldn't exist once DrawableHitObject's ClearTransformsAfter overrides are repaired.
|
||||
f.LifetimeStart = Time.Current;
|
||||
f.Expire();
|
||||
}
|
||||
foreach (var f in caughtFruit.ToArray())
|
||||
Drop(f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -425,10 +417,26 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
Explode(f);
|
||||
}
|
||||
|
||||
public void Drop(DrawableHitObject fruit) => removeFromPlateWithTransform(fruit, f =>
|
||||
{
|
||||
f.MoveToY(f.Y + 75, 750, Easing.InSine);
|
||||
f.FadeOut(750);
|
||||
});
|
||||
|
||||
public void Explode(DrawableHitObject fruit)
|
||||
{
|
||||
var originalX = fruit.X * Scale.X;
|
||||
|
||||
removeFromPlateWithTransform(fruit, f =>
|
||||
{
|
||||
f.MoveToY(f.Y - 50, 250, Easing.OutSine).Then().MoveToY(f.Y + 50, 500, Easing.InSine);
|
||||
f.MoveToX(f.X + originalX * 6, 1000);
|
||||
f.FadeOut(750);
|
||||
});
|
||||
}
|
||||
|
||||
private void removeFromPlateWithTransform(DrawableHitObject fruit, Action<DrawableHitObject> action)
|
||||
{
|
||||
if (ExplodingFruitTarget != null)
|
||||
{
|
||||
fruit.Anchor = Anchor.TopLeft;
|
||||
@ -442,25 +450,18 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
ExplodingFruitTarget.Add(fruit);
|
||||
}
|
||||
|
||||
fruit.ClearTransforms();
|
||||
fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine).Then().MoveToY(fruit.Y + 50, 500, Easing.InSine);
|
||||
fruit.MoveToX(fruit.X + originalX * 6, 1000);
|
||||
fruit.FadeOut(750);
|
||||
double actionTime = Clock.CurrentTime;
|
||||
|
||||
// todo: this shouldn't exist once DrawableHitObject's ClearTransformsAfter overrides are repaired.
|
||||
fruit.LifetimeStart = Time.Current;
|
||||
fruit.Expire();
|
||||
}
|
||||
fruit.ApplyCustomUpdateState += onFruitOnApplyCustomUpdateState;
|
||||
onFruitOnApplyCustomUpdateState(fruit, fruit.State.Value);
|
||||
|
||||
public void UpdatePosition(float position)
|
||||
{
|
||||
position = Math.Clamp(position, 0, 1);
|
||||
void onFruitOnApplyCustomUpdateState(DrawableHitObject o, ArmedState state)
|
||||
{
|
||||
using (fruit.BeginAbsoluteSequence(actionTime))
|
||||
action(fruit);
|
||||
|
||||
if (position == X)
|
||||
return;
|
||||
|
||||
Scale = new Vector2(Math.Abs(Scale.X) * (position > X ? 1 : -1), Scale.Y);
|
||||
X = position;
|
||||
fruit.Expire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,85 +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 System;
|
||||
using System.Text.RegularExpressions;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public abstract class SkinnableTestScene : OsuGridTestScene
|
||||
{
|
||||
private Skin metricsSkin;
|
||||
private Skin defaultSkin;
|
||||
private Skin specialSkin;
|
||||
private Skin oldSkin;
|
||||
|
||||
protected SkinnableTestScene()
|
||||
: base(2, 3)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, SkinManager skinManager)
|
||||
{
|
||||
var dllStore = new DllResourceStore(typeof(SkinnableTestScene).Assembly);
|
||||
|
||||
metricsSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), audio, true);
|
||||
defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
|
||||
specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/special_skin"), audio, true);
|
||||
oldSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/old_skin"), audio, true);
|
||||
}
|
||||
|
||||
public void SetContents(Func<Drawable> creationFunction)
|
||||
{
|
||||
Cell(0).Child = createProvider(null, creationFunction);
|
||||
Cell(1).Child = createProvider(metricsSkin, creationFunction);
|
||||
Cell(2).Child = createProvider(defaultSkin, creationFunction);
|
||||
Cell(3).Child = createProvider(specialSkin, creationFunction);
|
||||
Cell(4).Child = createProvider(oldSkin, creationFunction);
|
||||
}
|
||||
|
||||
private Drawable createProvider(Skin skin, Func<Drawable> creationFunction)
|
||||
{
|
||||
var mainProvider = new SkinProvidingContainer(skin);
|
||||
|
||||
return mainProvider
|
||||
.WithChild(new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider))
|
||||
{
|
||||
Child = creationFunction()
|
||||
});
|
||||
}
|
||||
|
||||
private class TestLegacySkin : LegacySkin
|
||||
{
|
||||
private readonly bool extrapolateAnimations;
|
||||
|
||||
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager, bool extrapolateAnimations)
|
||||
: base(skin, storage, audioManager, "skin.ini")
|
||||
{
|
||||
this.extrapolateAnimations = extrapolateAnimations;
|
||||
}
|
||||
|
||||
public override Texture GetTexture(string componentName)
|
||||
{
|
||||
// extrapolate frames to test longer animations
|
||||
if (extrapolateAnimations)
|
||||
{
|
||||
var match = Regex.Match(componentName, "-([0-9]*)");
|
||||
|
||||
if (match.Length > 0 && int.TryParse(match.Groups[1].Value, out var number) && number < 60)
|
||||
return base.GetTexture(componentName.Replace($"-{number}", $"-{number % 2}"));
|
||||
}
|
||||
|
||||
return base.GetTexture(componentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Testing.Input;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
@ -14,6 +14,7 @@ using osu.Game.Rulesets.Mods;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
|
@ -22,6 +22,7 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
|
BIN
osu.Game.Tests/Resources/Archives/ogg-beatmap.osz
Normal file
BIN
osu.Game.Tests/Resources/Archives/ogg-skin.osk
Normal file
37
osu.Game.Tests/Skins/TestSceneBeatmapSkinResources.cs
Normal file
@ -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 NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Skins
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneBeatmapSkinResources : OsuTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
|
||||
private WorkingBeatmap beatmap;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var imported = beatmaps.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-beatmap.osz"))).Result;
|
||||
beatmap = beatmaps.GetWorkingBeatmap(imported.Beatmaps[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRetrieveOggSample() => AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo("sample")) != null);
|
||||
|
||||
[Test]
|
||||
public void TestRetrieveOggTrack() => AddAssert("track is non-null", () => !(beatmap.Track is TrackVirtual));
|
||||
}
|
||||
}
|
33
osu.Game.Tests/Skins/TestSceneSkinResources.cs
Normal file
@ -0,0 +1,33 @@
|
||||
// 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.Testing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Skins
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneSkinResources : OsuTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; }
|
||||
|
||||
private ISkin skin;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var imported = skins.Import(new ZipArchiveReader(TestResources.OpenResource("Archives/ogg-skin.osk"))).Result;
|
||||
skin = skins.GetSkin(imported);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRetrieveOggSample() => AddAssert("sample is non-null", () => skin.GetSample(new SampleInfo("sample")) != null);
|
||||
}
|
||||
}
|
@ -134,6 +134,22 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("playlist has 2 items", () => Room.Playlist.Count == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAddItemAfterRearrangement()
|
||||
{
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("rearrange", () =>
|
||||
{
|
||||
var item = Room.Playlist[0];
|
||||
Room.Playlist.RemoveAt(0);
|
||||
Room.Playlist.Add(item);
|
||||
});
|
||||
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddAssert("new item has id 2", () => Room.Playlist.Last().ID == 2);
|
||||
}
|
||||
|
||||
private class TestMatchSongSelect : MatchSongSelect
|
||||
{
|
||||
public new MatchBeatmapDetailArea BeatmapDetails => (MatchBeatmapDetailArea)base.BeatmapDetails;
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
typeof(CommentsHeader),
|
||||
typeof(DrawableComment),
|
||||
typeof(HeaderButton),
|
||||
typeof(SortTabControl),
|
||||
typeof(OverlaySortTabControl<>),
|
||||
typeof(ShowChildrenButton),
|
||||
typeof(DeletedCommentsCounter),
|
||||
typeof(VotePill),
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
typeof(CommentsHeader),
|
||||
typeof(HeaderButton),
|
||||
typeof(SortTabControl),
|
||||
typeof(OverlaySortTabControl<>),
|
||||
};
|
||||
|
||||
[Cached]
|
||||
|
@ -0,0 +1,55 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public class TestSceneBeatmapListingSort : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(BeatmapListingSortTabControl),
|
||||
typeof(OverlaySortTabControl<>),
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
public TestSceneBeatmapListingSort()
|
||||
{
|
||||
BeatmapListingSortTabControl control;
|
||||
OsuSpriteText current;
|
||||
OsuSpriteText direction;
|
||||
|
||||
Add(control = new BeatmapListingSortTabControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
|
||||
Add(new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
current = new OsuSpriteText(),
|
||||
direction = new OsuSpriteText()
|
||||
}
|
||||
});
|
||||
|
||||
control.SortDirection.BindValueChanged(sortDirection => direction.Text = $"Sort direction: {sortDirection.NewValue}", true);
|
||||
control.Current.BindValueChanged(criteria => current.Text = $"Criteria: {criteria.NewValue}", true);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapListing;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public class TestSceneBeatmapSearchFilter : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(BeatmapSearchFilterRow<>),
|
||||
typeof(BeatmapSearchRulesetFilterRow),
|
||||
typeof(BeatmapSearchSmallFilterRow<>),
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
private readonly ReverseChildIDFillFlowContainer<Drawable> resizableContainer;
|
||||
|
||||
public TestSceneBeatmapSearchFilter()
|
||||
{
|
||||
Add(resizableContainer = new ReverseChildIDFillFlowContainer<Drawable>
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new BeatmapSearchRulesetFilterRow(),
|
||||
new BeatmapSearchFilterRow<BeatmapSearchCategory>("Categories"),
|
||||
new BeatmapSearchSmallFilterRow<BeatmapSearchCategory>("Header Name")
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestResize()
|
||||
{
|
||||
AddStep("Resize to 0.3", () => resizableContainer.ResizeWidthTo(0.3f, 1000));
|
||||
AddStep("Resize to 1", () => resizableContainer.ResizeWidthTo(1, 1000));
|
||||
}
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
private readonly Box strip;
|
||||
|
||||
protected override Dropdown<T> CreateDropdown() => new OsuTabDropdown();
|
||||
protected override Dropdown<T> CreateDropdown() => new OsuTabDropdown<T>();
|
||||
|
||||
protected override TabItem<T> CreateTabItem(T value) => new OsuTabItem(value);
|
||||
|
||||
@ -180,100 +180,5 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
protected override void OnDeactivated() => fadeInactive();
|
||||
}
|
||||
|
||||
// todo: this needs to go
|
||||
private class OsuTabDropdown : OsuDropdown<T>
|
||||
{
|
||||
public OsuTabDropdown()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
}
|
||||
|
||||
protected override DropdownMenu CreateMenu() => new OsuTabDropdownMenu();
|
||||
|
||||
protected override DropdownHeader CreateHeader() => new OsuTabDropdownHeader
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight
|
||||
};
|
||||
|
||||
private class OsuTabDropdownMenu : OsuDropdownMenu
|
||||
{
|
||||
public OsuTabDropdownMenu()
|
||||
{
|
||||
Anchor = Anchor.TopRight;
|
||||
Origin = Anchor.TopRight;
|
||||
|
||||
BackgroundColour = Color4.Black.Opacity(0.7f);
|
||||
MaxHeight = 400;
|
||||
}
|
||||
|
||||
protected override DrawableDropdownMenuItem CreateDrawableDropdownMenuItem(MenuItem item) => new DrawableOsuTabDropdownMenuItem(item) { AccentColour = AccentColour };
|
||||
|
||||
private class DrawableOsuTabDropdownMenuItem : DrawableOsuDropdownMenuItem
|
||||
{
|
||||
public DrawableOsuTabDropdownMenuItem(MenuItem item)
|
||||
: base(item)
|
||||
{
|
||||
ForegroundColourHover = Color4.Black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class OsuTabDropdownHeader : OsuDropdownHeader
|
||||
{
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get => base.AccentColour;
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
Foreground.Colour = value;
|
||||
}
|
||||
}
|
||||
|
||||
public OsuTabDropdownHeader()
|
||||
{
|
||||
RelativeSizeAxes = Axes.None;
|
||||
AutoSizeAxes = Axes.X;
|
||||
|
||||
BackgroundColour = Color4.Black.Opacity(0.5f);
|
||||
|
||||
Background.Height = 0.5f;
|
||||
Background.CornerRadius = 5;
|
||||
Background.Masking = true;
|
||||
|
||||
Foreground.RelativeSizeAxes = Axes.None;
|
||||
Foreground.AutoSizeAxes = Axes.X;
|
||||
Foreground.RelativeSizeAxes = Axes.Y;
|
||||
Foreground.Margin = new MarginPadding(5);
|
||||
|
||||
Foreground.Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.Solid.EllipsisH,
|
||||
Size = new Vector2(14),
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
}
|
||||
};
|
||||
|
||||
Padding = new MarginPadding { Left = 5, Right = 5 };
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
Foreground.Colour = BackgroundColour;
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
Foreground.Colour = BackgroundColourHover;
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
107
osu.Game/Graphics/UserInterface/OsuTabDropdown.cs
Normal file
@ -0,0 +1,107 @@
|
||||
// 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 osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public class OsuTabDropdown<T> : OsuDropdown<T>
|
||||
{
|
||||
public OsuTabDropdown()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
}
|
||||
|
||||
protected override DropdownMenu CreateMenu() => new OsuTabDropdownMenu();
|
||||
|
||||
protected override DropdownHeader CreateHeader() => new OsuTabDropdownHeader
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight
|
||||
};
|
||||
|
||||
private class OsuTabDropdownMenu : OsuDropdownMenu
|
||||
{
|
||||
public OsuTabDropdownMenu()
|
||||
{
|
||||
Anchor = Anchor.TopRight;
|
||||
Origin = Anchor.TopRight;
|
||||
|
||||
BackgroundColour = Color4.Black.Opacity(0.7f);
|
||||
MaxHeight = 400;
|
||||
}
|
||||
|
||||
protected override DrawableDropdownMenuItem CreateDrawableDropdownMenuItem(MenuItem item) => new DrawableOsuTabDropdownMenuItem(item) { AccentColour = AccentColour };
|
||||
|
||||
private class DrawableOsuTabDropdownMenuItem : DrawableOsuDropdownMenuItem
|
||||
{
|
||||
public DrawableOsuTabDropdownMenuItem(MenuItem item)
|
||||
: base(item)
|
||||
{
|
||||
ForegroundColourHover = Color4.Black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class OsuTabDropdownHeader : OsuDropdownHeader
|
||||
{
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get => base.AccentColour;
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
Foreground.Colour = value;
|
||||
}
|
||||
}
|
||||
|
||||
public OsuTabDropdownHeader()
|
||||
{
|
||||
RelativeSizeAxes = Axes.None;
|
||||
AutoSizeAxes = Axes.X;
|
||||
|
||||
BackgroundColour = Color4.Black.Opacity(0.5f);
|
||||
|
||||
Background.Height = 0.5f;
|
||||
Background.CornerRadius = 5;
|
||||
Background.Masking = true;
|
||||
|
||||
Foreground.RelativeSizeAxes = Axes.None;
|
||||
Foreground.AutoSizeAxes = Axes.X;
|
||||
Foreground.RelativeSizeAxes = Axes.Y;
|
||||
Foreground.Margin = new MarginPadding(5);
|
||||
|
||||
Foreground.Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.Solid.EllipsisH,
|
||||
Size = new Vector2(14),
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
}
|
||||
};
|
||||
|
||||
Padding = new MarginPadding { Left = 5, Right = 5 };
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
Foreground.Colour = BackgroundColour;
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
Foreground.Colour = BackgroundColourHover;
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
118
osu.Game/Overlays/BeatmapListing/BeatmapListingSortTabControl.cs
Normal file
@ -0,0 +1,118 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Graphics;
|
||||
using osuTK.Graphics;
|
||||
using osuTK;
|
||||
using osu.Framework.Input.Events;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public class BeatmapListingSortTabControl : OverlaySortTabControl<BeatmapSortCriteria>
|
||||
{
|
||||
public readonly Bindable<SortDirection> SortDirection = new Bindable<SortDirection>(Overlays.SortDirection.Descending);
|
||||
|
||||
public BeatmapListingSortTabControl()
|
||||
{
|
||||
Current.Value = BeatmapSortCriteria.Ranked;
|
||||
}
|
||||
|
||||
protected override SortTabControl CreateControl() => new BeatmapSortTabControl
|
||||
{
|
||||
SortDirection = { BindTarget = SortDirection }
|
||||
};
|
||||
|
||||
private class BeatmapSortTabControl : SortTabControl
|
||||
{
|
||||
public readonly Bindable<SortDirection> SortDirection = new Bindable<SortDirection>();
|
||||
|
||||
protected override TabItem<BeatmapSortCriteria> CreateTabItem(BeatmapSortCriteria value) => new BeatmapSortTabItem(value)
|
||||
{
|
||||
SortDirection = { BindTarget = SortDirection }
|
||||
};
|
||||
}
|
||||
|
||||
private class BeatmapSortTabItem : SortTabItem
|
||||
{
|
||||
public readonly Bindable<SortDirection> SortDirection = new Bindable<SortDirection>();
|
||||
|
||||
public BeatmapSortTabItem(BeatmapSortCriteria value)
|
||||
: base(value)
|
||||
{
|
||||
}
|
||||
|
||||
protected override TabButton CreateTabButton(BeatmapSortCriteria value) => new BeatmapTabButton(value)
|
||||
{
|
||||
Active = { BindTarget = Active },
|
||||
SortDirection = { BindTarget = SortDirection }
|
||||
};
|
||||
}
|
||||
|
||||
private class BeatmapTabButton : TabButton
|
||||
{
|
||||
public readonly Bindable<SortDirection> SortDirection = new Bindable<SortDirection>();
|
||||
|
||||
protected override Color4 ContentColour
|
||||
{
|
||||
set
|
||||
{
|
||||
base.ContentColour = value;
|
||||
icon.Colour = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly SpriteIcon icon;
|
||||
|
||||
public BeatmapTabButton(BeatmapSortCriteria value)
|
||||
: base(value)
|
||||
{
|
||||
Add(icon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
Size = new Vector2(6)
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
SortDirection.BindValueChanged(direction =>
|
||||
{
|
||||
icon.Icon = direction.NewValue == Overlays.SortDirection.Ascending ? FontAwesome.Solid.CaretUp : FontAwesome.Solid.CaretDown;
|
||||
}, true);
|
||||
}
|
||||
|
||||
protected override void UpdateState()
|
||||
{
|
||||
base.UpdateState();
|
||||
icon.FadeTo(Active.Value || IsHovered ? 1 : 0, 200, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
if (Active.Value)
|
||||
SortDirection.Value = SortDirection.Value == Overlays.SortDirection.Ascending ? Overlays.SortDirection.Descending : Overlays.SortDirection.Ascending;
|
||||
|
||||
return base.OnClick(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum BeatmapSortCriteria
|
||||
{
|
||||
Title,
|
||||
Artist,
|
||||
Difficulty,
|
||||
Ranked,
|
||||
Rating,
|
||||
Plays,
|
||||
Favourites,
|
||||
}
|
||||
}
|
173
osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs
Normal file
@ -0,0 +1,173 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public class BeatmapSearchFilterRow<T> : CompositeDrawable, IHasCurrentValue<T>
|
||||
{
|
||||
private readonly BindableWithCurrent<T> current = new BindableWithCurrent<T>();
|
||||
|
||||
public Bindable<T> Current
|
||||
{
|
||||
get => current.Current;
|
||||
set => current.Current = value;
|
||||
}
|
||||
|
||||
public BeatmapSearchFilterRow(string headerName)
|
||||
{
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AddInternal(new GridContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.Absolute, size: 100),
|
||||
new Dimension()
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Font = OsuFont.GetFont(size: 10),
|
||||
Text = headerName.ToUpper()
|
||||
},
|
||||
CreateFilter().With(f =>
|
||||
{
|
||||
f.Current = current;
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
protected virtual BeatmapSearchFilter CreateFilter() => new BeatmapSearchFilter();
|
||||
|
||||
protected class BeatmapSearchFilter : TabControl<T>
|
||||
{
|
||||
public BeatmapSearchFilter()
|
||||
{
|
||||
Anchor = Anchor.BottomLeft;
|
||||
Origin = Anchor.BottomLeft;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 15;
|
||||
|
||||
TabContainer.Spacing = new Vector2(10, 0);
|
||||
|
||||
if (typeof(T).IsEnum)
|
||||
{
|
||||
foreach (var val in (T[])Enum.GetValues(typeof(T)))
|
||||
AddItem(val);
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
((FilterDropdown)Dropdown).AccentColour = colourProvider.Light2;
|
||||
}
|
||||
|
||||
protected override Dropdown<T> CreateDropdown() => new FilterDropdown();
|
||||
|
||||
protected override TabItem<T> CreateTabItem(T value) => new FilterTabItem(value);
|
||||
|
||||
protected class FilterTabItem : TabItem<T>
|
||||
{
|
||||
protected virtual float TextSize => 13;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; }
|
||||
|
||||
private readonly OsuSpriteText text;
|
||||
|
||||
public FilterTabItem(T value)
|
||||
: base(value)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Anchor = Anchor.BottomLeft;
|
||||
Origin = Anchor.BottomLeft;
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
text = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Regular),
|
||||
Text = (value as Enum)?.GetDescription() ?? value.ToString()
|
||||
},
|
||||
new HoverClickSounds()
|
||||
});
|
||||
|
||||
Enabled.Value = true;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
updateState();
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
base.OnHover(e);
|
||||
updateState();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
base.OnHoverLost(e);
|
||||
updateState();
|
||||
}
|
||||
|
||||
protected override void OnActivated() => updateState();
|
||||
|
||||
protected override void OnDeactivated() => updateState();
|
||||
|
||||
private void updateState() => text.FadeColour(Active.Value ? Color4.White : getStateColour(), 200, Easing.OutQuint);
|
||||
|
||||
private Color4 getStateColour() => IsHovered ? colourProvider.Light1 : colourProvider.Light3;
|
||||
}
|
||||
|
||||
private class FilterDropdown : OsuTabDropdown<T>
|
||||
{
|
||||
protected override DropdownHeader CreateHeader() => new FilterHeader
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight
|
||||
};
|
||||
|
||||
private class FilterHeader : OsuTabDropdownHeader
|
||||
{
|
||||
public FilterHeader()
|
||||
{
|
||||
Background.Height = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
// 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.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public class BeatmapSearchRulesetFilterRow : BeatmapSearchFilterRow<RulesetInfo>
|
||||
{
|
||||
public BeatmapSearchRulesetFilterRow()
|
||||
: base(@"Mode")
|
||||
{
|
||||
}
|
||||
|
||||
protected override BeatmapSearchFilter CreateFilter() => new RulesetFilter();
|
||||
|
||||
private class RulesetFilter : BeatmapSearchFilter
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
AddItem(new RulesetInfo
|
||||
{
|
||||
Name = @"Any"
|
||||
});
|
||||
|
||||
foreach (var r in rulesets.AvailableRulesets)
|
||||
AddItem(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
public class BeatmapSearchSmallFilterRow<T> : BeatmapSearchFilterRow<T>
|
||||
{
|
||||
public BeatmapSearchSmallFilterRow(string headerName)
|
||||
: base(headerName)
|
||||
{
|
||||
}
|
||||
|
||||
protected override BeatmapSearchFilter CreateFilter() => new SmallBeatmapSearchFilter();
|
||||
|
||||
private class SmallBeatmapSearchFilter : BeatmapSearchFilter
|
||||
{
|
||||
protected override TabItem<T> CreateTabItem(T value) => new SmallTabItem(value);
|
||||
|
||||
private class SmallTabItem : FilterTabItem
|
||||
{
|
||||
public SmallTabItem(T value)
|
||||
: base(value)
|
||||
{
|
||||
}
|
||||
|
||||
protected override float TextSize => 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -16,8 +16,6 @@ namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
public class CommentsHeader : CompositeDrawable
|
||||
{
|
||||
private const int font_size = 14;
|
||||
|
||||
public readonly Bindable<CommentsSortCriteria> Sort = new Bindable<CommentsSortCriteria>();
|
||||
public readonly BindableBool ShowDeleted = new BindableBool();
|
||||
|
||||
@ -40,29 +38,11 @@ namespace osu.Game.Overlays.Comments
|
||||
Padding = new MarginPadding { Horizontal = 50 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
new OverlaySortTabControl<CommentsSortCriteria>
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: font_size),
|
||||
Text = @"Sort by"
|
||||
},
|
||||
new SortTabControl
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Current = Sort
|
||||
}
|
||||
}
|
||||
Current = Sort
|
||||
},
|
||||
new ShowDeletedButton
|
||||
{
|
||||
@ -106,7 +86,7 @@ namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: font_size),
|
||||
Font = OsuFont.GetFont(size: 12),
|
||||
Text = @"Show deleted"
|
||||
}
|
||||
},
|
||||
@ -126,4 +106,11 @@ namespace osu.Game.Overlays.Comments
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum CommentsSortCriteria
|
||||
{
|
||||
New,
|
||||
Old,
|
||||
Top
|
||||
}
|
||||
}
|
||||
|
@ -1,110 +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 osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
public class SortTabControl : OsuTabControl<CommentsSortCriteria>
|
||||
{
|
||||
protected override Dropdown<CommentsSortCriteria> CreateDropdown() => null;
|
||||
|
||||
protected override TabItem<CommentsSortCriteria> CreateTabItem(CommentsSortCriteria value) => new SortTabItem(value);
|
||||
|
||||
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5, 0),
|
||||
};
|
||||
|
||||
public SortTabControl()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
private class SortTabItem : TabItem<CommentsSortCriteria>
|
||||
{
|
||||
public SortTabItem(CommentsSortCriteria value)
|
||||
: base(value)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Child = new TabButton(value) { Active = { BindTarget = Active } };
|
||||
}
|
||||
|
||||
protected override void OnActivated()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnDeactivated()
|
||||
{
|
||||
}
|
||||
|
||||
private class TabButton : HeaderButton
|
||||
{
|
||||
public readonly BindableBool Active = new BindableBool();
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; }
|
||||
|
||||
private readonly SpriteText text;
|
||||
|
||||
public TabButton(CommentsSortCriteria value)
|
||||
{
|
||||
Add(text = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 14),
|
||||
Text = value.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Active.BindValueChanged(active =>
|
||||
{
|
||||
updateBackgroundState();
|
||||
|
||||
text.Font = text.Font.With(weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium);
|
||||
text.Colour = active.NewValue ? colourProvider.Light1 : Color4.White;
|
||||
}, true);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
updateBackgroundState();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e) => updateBackgroundState();
|
||||
|
||||
private void updateBackgroundState()
|
||||
{
|
||||
if (Active.Value || IsHovered)
|
||||
ShowBackground();
|
||||
else
|
||||
HideBackground();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum CommentsSortCriteria
|
||||
{
|
||||
New,
|
||||
Old,
|
||||
Top
|
||||
}
|
||||
}
|
168
osu.Game/Overlays/OverlaySortTabControl.cs
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Overlays.Comments;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class OverlaySortTabControl<T> : CompositeDrawable, IHasCurrentValue<T>
|
||||
{
|
||||
private readonly BindableWithCurrent<T> current = new BindableWithCurrent<T>();
|
||||
|
||||
public Bindable<T> Current
|
||||
{
|
||||
get => current.Current;
|
||||
set => current.Current = value;
|
||||
}
|
||||
|
||||
public OverlaySortTabControl()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
AddInternal(new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: 12),
|
||||
Text = @"Sort by"
|
||||
},
|
||||
CreateControl().With(c =>
|
||||
{
|
||||
c.Anchor = Anchor.CentreLeft;
|
||||
c.Origin = Anchor.CentreLeft;
|
||||
c.Current = current;
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
protected virtual SortTabControl CreateControl() => new SortTabControl();
|
||||
|
||||
protected class SortTabControl : OsuTabControl<T>
|
||||
{
|
||||
protected override Dropdown<T> CreateDropdown() => null;
|
||||
|
||||
protected override TabItem<T> CreateTabItem(T value) => new SortTabItem(value);
|
||||
|
||||
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5, 0),
|
||||
};
|
||||
|
||||
public SortTabControl()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
}
|
||||
|
||||
protected class SortTabItem : TabItem<T>
|
||||
{
|
||||
public SortTabItem(T value)
|
||||
: base(value)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Child = CreateTabButton(value);
|
||||
}
|
||||
|
||||
[NotNull]
|
||||
protected virtual TabButton CreateTabButton(T value) => new TabButton(value)
|
||||
{
|
||||
Active = { BindTarget = Active }
|
||||
};
|
||||
|
||||
protected override void OnActivated()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnDeactivated()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected class TabButton : HeaderButton
|
||||
{
|
||||
public readonly BindableBool Active = new BindableBool();
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
protected virtual Color4 ContentColour
|
||||
{
|
||||
set => text.Colour = value;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; }
|
||||
|
||||
private readonly SpriteText text;
|
||||
private readonly FillFlowContainer content;
|
||||
|
||||
public TabButton(T value)
|
||||
{
|
||||
base.Content.Add(content = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(3, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
text = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Font = OsuFont.GetFont(size: 12),
|
||||
Text = value.ToString()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Active.BindValueChanged(_ => UpdateState(), true);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
UpdateState();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e) => UpdateState();
|
||||
|
||||
protected virtual void UpdateState()
|
||||
{
|
||||
if (Active.Value || IsHovered)
|
||||
ShowBackground();
|
||||
else
|
||||
HideBackground();
|
||||
|
||||
ContentColour = Active.Value && !IsHovered ? colourProvider.Light1 : Color4.White;
|
||||
|
||||
text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Rankings
|
||||
private void getSpotlights()
|
||||
{
|
||||
spotlightsRequest = new GetSpotlightsRequest();
|
||||
spotlightsRequest.Success += response => selector.Spotlights = response.Spotlights;
|
||||
spotlightsRequest.Success += response => Schedule(() => selector.Spotlights = response.Spotlights);
|
||||
api.Queue(spotlightsRequest);
|
||||
}
|
||||
|
||||
@ -151,11 +151,11 @@ namespace osu.Game.Overlays.Rankings
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
spotlightsRequest?.Cancel();
|
||||
getRankingsRequest?.Cancel();
|
||||
cancellationToken?.Cancel();
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Game.Users;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
|
||||
namespace osu.Game.Overlays.Rankings.Tables
|
||||
{
|
||||
@ -30,11 +31,7 @@ namespace osu.Game.Overlays.Rankings.Tables
|
||||
|
||||
protected override Country GetCountry(CountryStatistics item) => item.Country;
|
||||
|
||||
protected override Drawable CreateFlagContent(CountryStatistics item) => new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: TEXT_SIZE),
|
||||
Text = $@"{item.Country.FullName}",
|
||||
};
|
||||
protected override Drawable CreateFlagContent(CountryStatistics item) => new CountryName(item.Country);
|
||||
|
||||
protected override Drawable[] CreateAdditionalContent(CountryStatistics item) => new Drawable[]
|
||||
{
|
||||
@ -63,5 +60,20 @@ namespace osu.Game.Overlays.Rankings.Tables
|
||||
Text = $@"{item.Performance / Math.Max(item.ActiveUsers, 1):N0}",
|
||||
}
|
||||
};
|
||||
|
||||
private class CountryName : OsuSpriteText
|
||||
{
|
||||
public CountryName(Country country)
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 12);
|
||||
Text = country.FullName ?? string.Empty;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
Colour = colourProvider.Light2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Rankings.Tables
|
||||
|
||||
protected sealed override Drawable CreateFlagContent(UserStatistics item)
|
||||
{
|
||||
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE)) { AutoSizeAxes = Axes.Both };
|
||||
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE, italics: true)) { AutoSizeAxes = Axes.Both };
|
||||
username.AddUserLink(item.User);
|
||||
return username;
|
||||
}
|
||||
|
@ -50,6 +50,6 @@ namespace osu.Game.Rulesets
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Name} ({ShortName}) ID: {ID}";
|
||||
public override string ToString() => Name ?? $"{Name} ({ShortName}) ID: {ID}";
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
PlaylistItem item = new PlaylistItem
|
||||
{
|
||||
ID = (Playlist.LastOrDefault()?.ID + 1) ?? 0,
|
||||
ID = Playlist.Count == 0 ? 0 : Playlist.Max(p => p.ID) + 1
|
||||
};
|
||||
|
||||
populateItemFromCurrent(item);
|
||||
|
@ -54,6 +54,8 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
Samples = audioManager?.GetSampleStore(storage);
|
||||
Textures = new TextureStore(new TextureLoaderStore(storage));
|
||||
|
||||
(storage as ResourceStore<byte[]>)?.AddExtension("ogg");
|
||||
}
|
||||
}
|
||||
|
||||
|
160
osu.Game/Tests/Visual/SkinnableTestScene.cs
Normal file
@ -0,0 +1,160 @@
|
||||
// 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.Text.RegularExpressions;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public abstract class SkinnableTestScene : OsuGridTestScene
|
||||
{
|
||||
private Skin metricsSkin;
|
||||
private Skin defaultSkin;
|
||||
private Skin specialSkin;
|
||||
private Skin oldSkin;
|
||||
|
||||
protected SkinnableTestScene()
|
||||
: base(2, 3)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, SkinManager skinManager)
|
||||
{
|
||||
var dllStore = new DllResourceStore(GetType().Assembly);
|
||||
|
||||
metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), audio, true);
|
||||
defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
|
||||
specialSkin = new TestLegacySkin(new SkinInfo { Name = "special-skin" }, new NamespacedResourceStore<byte[]>(dllStore, "Resources/special_skin"), audio, true);
|
||||
oldSkin = new TestLegacySkin(new SkinInfo { Name = "old-skin" }, new NamespacedResourceStore<byte[]>(dllStore, "Resources/old_skin"), audio, true);
|
||||
}
|
||||
|
||||
private readonly List<Drawable> createdDrawables = new List<Drawable>();
|
||||
|
||||
public void SetContents(Func<Drawable> creationFunction)
|
||||
{
|
||||
createdDrawables.Clear();
|
||||
|
||||
Cell(0).Child = createProvider(null, creationFunction);
|
||||
Cell(1).Child = createProvider(metricsSkin, creationFunction);
|
||||
Cell(2).Child = createProvider(defaultSkin, creationFunction);
|
||||
Cell(3).Child = createProvider(specialSkin, creationFunction);
|
||||
Cell(4).Child = createProvider(oldSkin, creationFunction);
|
||||
}
|
||||
|
||||
protected IEnumerable<Drawable> CreatedDrawables => createdDrawables;
|
||||
|
||||
private Drawable createProvider(Skin skin, Func<Drawable> creationFunction)
|
||||
{
|
||||
var created = creationFunction();
|
||||
createdDrawables.Add(created);
|
||||
|
||||
var autoSize = created.RelativeSizeAxes == Axes.None;
|
||||
|
||||
var mainProvider = new SkinProvidingContainer(skin)
|
||||
{
|
||||
RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None,
|
||||
AutoSizeAxes = autoSize ? Axes.Both : Axes.None,
|
||||
};
|
||||
|
||||
return new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BorderColour = Color4.White,
|
||||
BorderThickness = 5,
|
||||
Masking = true,
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = skin?.SkinInfo?.Name ?? "none",
|
||||
Scale = new Vector2(1.5f),
|
||||
Padding = new MarginPadding(5),
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None,
|
||||
AutoSizeAxes = autoSize ? Axes.Both : Axes.None,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OutlineBox { Alpha = autoSize ? 1 : 0 },
|
||||
mainProvider.WithChild(
|
||||
new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider))
|
||||
{
|
||||
Child = created,
|
||||
RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None,
|
||||
AutoSizeAxes = autoSize ? Axes.Both : Axes.None,
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class OutlineBox : CompositeDrawable
|
||||
{
|
||||
public OutlineBox()
|
||||
{
|
||||
BorderColour = Color4.IndianRed;
|
||||
BorderThickness = 5;
|
||||
Masking = true;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Colour = Color4.Brown,
|
||||
AlwaysPresent = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class TestLegacySkin : LegacySkin
|
||||
{
|
||||
private readonly bool extrapolateAnimations;
|
||||
|
||||
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager, bool extrapolateAnimations)
|
||||
: base(skin, storage, audioManager, "skin.ini")
|
||||
{
|
||||
this.extrapolateAnimations = extrapolateAnimations;
|
||||
}
|
||||
|
||||
public override Texture GetTexture(string componentName)
|
||||
{
|
||||
// extrapolate frames to test longer animations
|
||||
if (extrapolateAnimations)
|
||||
{
|
||||
var match = Regex.Match(componentName, "-([0-9]*)");
|
||||
|
||||
if (match.Length > 0 && int.TryParse(match.Groups[1].Value, out var number) && number < 60)
|
||||
return base.GetTexture(componentName.Replace($"-{number}", $"-{number % 2}"));
|
||||
}
|
||||
|
||||
return base.GetTexture(componentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.216.0" />
|
||||
<PackageReference Include="Sentry" Version="2.0.2" />
|
||||
<PackageReference Include="Sentry" Version="2.0.3" />
|
||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
|
||||
|
@ -87,6 +87,6 @@
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2019.1104.0" ExcludeAssets="all" />
|
||||
<PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2020.213.0" ExcludeAssets="all" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|