mirror of
https://github.com/osukey/osukey.git
synced 2025-05-19 20:47:24 +09:00
Merge branch 'master' into consume-bindable-current-factory
This commit is contained in:
commit
f5166d8dd5
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
A free-to-win rhythm game. Rhythm is just a *click* away!
|
A free-to-win rhythm game. Rhythm is just a *click* away!
|
||||||
|
|
||||||
The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Commonly known by the codename *osu!lazer*. Pew pew.
|
The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Currently known by and released under the codename "*lazer*". As in sharper than cutting-edge.
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ We are accepting bug reports (please report with as much detail as possible and
|
|||||||
|
|
||||||
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
|
- 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).
|
- 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.
|
- Read peppy's [latest blog post](https://blog.ppy.sh/a-definitive-lazer-faq/) exploring where the project is currently and the roadmap going forward.
|
||||||
|
|
||||||
## Running osu!
|
## Running osu!
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ using osu.Game.Rulesets.Catch.Objects;
|
|||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
||||||
{
|
{
|
||||||
@ -23,5 +24,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
: base(new THitObject())
|
: base(new THitObject())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
public abstract class CatchSelectionBlueprint<THitObject> : HitObjectSelectionBlueprint<THitObject>
|
public abstract class CatchSelectionBlueprint<THitObject> : HitObjectSelectionBlueprint<THitObject>
|
||||||
where THitObject : CatchHitObject
|
where THitObject : CatchHitObject
|
||||||
{
|
{
|
||||||
|
protected override bool AlwaysShowWhenSelected => true;
|
||||||
|
|
||||||
public override Vector2 ScreenSpaceSelectionPoint
|
public override Vector2 ScreenSpaceSelectionPoint
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -18,7 +19,6 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft;
|
Anchor = Anchor.BottomLeft;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
Size = new Vector2(2 * CatchHitObject.OBJECT_RADIUS);
|
|
||||||
InternalChild = new BorderPiece();
|
InternalChild = new BorderPiece();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,10 +28,10 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
|||||||
Colour = osuColour.Yellow;
|
Colour = osuColour.Yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject)
|
public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject, [CanBeNull] CatchHitObject parent = null)
|
||||||
{
|
{
|
||||||
X = hitObject.EffectiveX;
|
X = hitObject.EffectiveX - (parent?.OriginalX ?? 0);
|
||||||
Y = hitObjectContainer.PositionAtTime(hitObject.StartTime);
|
Y = hitObjectContainer.PositionAtTime(hitObject.StartTime, parent?.StartTime ?? hitObjectContainer.Time.Current);
|
||||||
Scale = new Vector2(hitObject.Scale);
|
Scale = new Vector2(hitObject.Scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
||||||
|
{
|
||||||
|
public class NestedOutlineContainer : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly List<CatchHitObject> nestedHitObjects = new List<CatchHitObject>();
|
||||||
|
|
||||||
|
public NestedOutlineContainer()
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject)
|
||||||
|
{
|
||||||
|
X = parentHitObject.OriginalX;
|
||||||
|
Y = hitObjectContainer.PositionAtTime(parentHitObject.StartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateNestedObjectsFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject)
|
||||||
|
{
|
||||||
|
nestedHitObjects.Clear();
|
||||||
|
nestedHitObjects.AddRange(parentHitObject.NestedHitObjects
|
||||||
|
.OfType<CatchHitObject>()
|
||||||
|
.Where(h => !(h is TinyDroplet)));
|
||||||
|
|
||||||
|
while (nestedHitObjects.Count < InternalChildren.Count)
|
||||||
|
RemoveInternal(InternalChildren[^1]);
|
||||||
|
|
||||||
|
while (InternalChildren.Count < nestedHitObjects.Count)
|
||||||
|
AddInternal(new FruitOutline());
|
||||||
|
|
||||||
|
for (int i = 0; i < nestedHitObjects.Count; i++)
|
||||||
|
{
|
||||||
|
var hitObject = nestedHitObjects[i];
|
||||||
|
var outline = (FruitOutline)InternalChildren[i];
|
||||||
|
outline.UpdateFrom(hitObjectContainer, hitObject, parentHitObject);
|
||||||
|
outline.Scale *= hitObject is Droplet ? 0.5f : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Lines;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
||||||
|
{
|
||||||
|
public class ScrollingPath : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly Path drawablePath;
|
||||||
|
|
||||||
|
private readonly List<(double Distance, float X)> vertices = new List<(double, float)>();
|
||||||
|
|
||||||
|
public ScrollingPath()
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
drawablePath = new SmoothPath
|
||||||
|
{
|
||||||
|
PathRadius = 2,
|
||||||
|
Alpha = 0.5f
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject)
|
||||||
|
{
|
||||||
|
X = hitObject.OriginalX;
|
||||||
|
Y = hitObjectContainer.PositionAtTime(hitObject.StartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePathFrom(ScrollingHitObjectContainer hitObjectContainer, JuiceStream hitObject)
|
||||||
|
{
|
||||||
|
double distanceToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1 / hitObject.Velocity);
|
||||||
|
|
||||||
|
computeDistanceXs(hitObject);
|
||||||
|
drawablePath.Vertices = vertices
|
||||||
|
.Select(v => new Vector2(v.X, (float)(v.Distance * distanceToYFactor)))
|
||||||
|
.ToArray();
|
||||||
|
drawablePath.OriginPosition = drawablePath.PositionInBoundingBox(Vector2.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void computeDistanceXs(JuiceStream hitObject)
|
||||||
|
{
|
||||||
|
vertices.Clear();
|
||||||
|
|
||||||
|
var sliderVertices = new List<Vector2>();
|
||||||
|
hitObject.Path.GetPathToProgress(sliderVertices, 0, 1);
|
||||||
|
|
||||||
|
if (sliderVertices.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
double distance = 0;
|
||||||
|
Vector2 lastPosition = Vector2.Zero;
|
||||||
|
|
||||||
|
for (int repeat = 0; repeat < hitObject.RepeatCount + 1; repeat++)
|
||||||
|
{
|
||||||
|
foreach (var position in sliderVertices)
|
||||||
|
{
|
||||||
|
distance += Vector2.Distance(lastPosition, position);
|
||||||
|
lastPosition = position;
|
||||||
|
|
||||||
|
vertices.Add((distance, position.X));
|
||||||
|
}
|
||||||
|
|
||||||
|
sliderVertices.Reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because this has 0x0 size, the contents are otherwise masked away if the start position is outside the screen.
|
||||||
|
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Caching;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -17,9 +20,20 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
private float minNestedX;
|
private float minNestedX;
|
||||||
private float maxNestedX;
|
private float maxNestedX;
|
||||||
|
|
||||||
|
private readonly ScrollingPath scrollingPath;
|
||||||
|
|
||||||
|
private readonly NestedOutlineContainer nestedOutlineContainer;
|
||||||
|
|
||||||
|
private readonly Cached pathCache = new Cached();
|
||||||
|
|
||||||
public JuiceStreamSelectionBlueprint(JuiceStream hitObject)
|
public JuiceStreamSelectionBlueprint(JuiceStream hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
scrollingPath = new ScrollingPath(),
|
||||||
|
nestedOutlineContainer = new NestedOutlineContainer()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -29,7 +43,28 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
computeObjectBounds();
|
computeObjectBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDefaultsApplied(HitObject _) => computeObjectBounds();
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (!IsSelected) return;
|
||||||
|
|
||||||
|
scrollingPath.UpdatePositionFrom(HitObjectContainer, HitObject);
|
||||||
|
nestedOutlineContainer.UpdatePositionFrom(HitObjectContainer, HitObject);
|
||||||
|
|
||||||
|
if (pathCache.IsValid) return;
|
||||||
|
|
||||||
|
scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject);
|
||||||
|
nestedOutlineContainer.UpdateNestedObjectsFrom(HitObjectContainer, HitObject);
|
||||||
|
|
||||||
|
pathCache.Validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDefaultsApplied(HitObject _)
|
||||||
|
{
|
||||||
|
computeObjectBounds();
|
||||||
|
pathCache.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
private void computeObjectBounds()
|
private void computeObjectBounds()
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
@ -20,6 +22,16 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
LayerBelowRuleset.Add(new PlayfieldBorder
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Corners }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected override DrawableRuleset<CatchHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null) =>
|
protected override DrawableRuleset<CatchHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null) =>
|
||||||
new DrawableCatchEditorRuleset(ruleset, beatmap, mods);
|
new DrawableCatchEditorRuleset(ruleset, beatmap, mods);
|
||||||
|
|
||||||
|
11
osu.Game.Tests/Resources/Shaders/sh_TestFragment.fs
Normal file
11
osu.Game.Tests/Resources/Shaders/sh_TestFragment.fs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "sh_Utils.h"
|
||||||
|
|
||||||
|
varying mediump vec2 v_TexCoord;
|
||||||
|
varying mediump vec4 v_TexRect;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
float hueValue = v_TexCoord.x / (v_TexRect[2] - v_TexRect[0]);
|
||||||
|
gl_FragColor = hsv2rgb(vec4(hueValue, 1, 1, 1));
|
||||||
|
}
|
||||||
|
|
31
osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs
Normal file
31
osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include "sh_Utils.h"
|
||||||
|
|
||||||
|
attribute highp vec2 m_Position;
|
||||||
|
attribute lowp vec4 m_Colour;
|
||||||
|
attribute mediump vec2 m_TexCoord;
|
||||||
|
attribute mediump vec4 m_TexRect;
|
||||||
|
attribute mediump vec2 m_BlendRange;
|
||||||
|
|
||||||
|
varying highp vec2 v_MaskingPosition;
|
||||||
|
varying lowp vec4 v_Colour;
|
||||||
|
varying mediump vec2 v_TexCoord;
|
||||||
|
varying mediump vec4 v_TexRect;
|
||||||
|
varying mediump vec2 v_BlendRange;
|
||||||
|
|
||||||
|
uniform highp mat4 g_ProjMatrix;
|
||||||
|
uniform highp mat3 g_ToMaskingSpace;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
// Transform from screen space to masking space.
|
||||||
|
highp vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0);
|
||||||
|
v_MaskingPosition = maskingPos.xy / maskingPos.z;
|
||||||
|
|
||||||
|
v_Colour = m_Colour;
|
||||||
|
v_TexCoord = m_TexCoord;
|
||||||
|
v_TexRect = m_TexRect;
|
||||||
|
v_BlendRange = m_BlendRange;
|
||||||
|
|
||||||
|
gl_Position = gProjMatrix * vec4(m_Position, 1.0, 1.0);
|
||||||
|
}
|
||||||
|
|
@ -13,8 +13,10 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.OpenGL.Textures;
|
using osu.Framework.Graphics.OpenGL.Textures;
|
||||||
|
using osu.Framework.Graphics.Shaders;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
@ -31,12 +33,14 @@ namespace osu.Game.Tests.Rulesets
|
|||||||
DrawableWithDependencies drawable = null;
|
DrawableWithDependencies drawable = null;
|
||||||
TestTextureStore textureStore = null;
|
TestTextureStore textureStore = null;
|
||||||
TestSampleStore sampleStore = null;
|
TestSampleStore sampleStore = null;
|
||||||
|
TestShaderManager shaderManager = null;
|
||||||
|
|
||||||
AddStep("add dependencies", () =>
|
AddStep("add dependencies", () =>
|
||||||
{
|
{
|
||||||
Child = drawable = new DrawableWithDependencies();
|
Child = drawable = new DrawableWithDependencies();
|
||||||
textureStore = drawable.ParentTextureStore;
|
textureStore = drawable.ParentTextureStore;
|
||||||
sampleStore = drawable.ParentSampleStore;
|
sampleStore = drawable.ParentSampleStore;
|
||||||
|
shaderManager = drawable.ParentShaderManager;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("clear children", Clear);
|
AddStep("clear children", Clear);
|
||||||
@ -52,12 +56,14 @@ namespace osu.Game.Tests.Rulesets
|
|||||||
|
|
||||||
AddAssert("parent texture store not disposed", () => !textureStore.IsDisposed);
|
AddAssert("parent texture store not disposed", () => !textureStore.IsDisposed);
|
||||||
AddAssert("parent sample store not disposed", () => !sampleStore.IsDisposed);
|
AddAssert("parent sample store not disposed", () => !sampleStore.IsDisposed);
|
||||||
|
AddAssert("parent shader manager not disposed", () => !shaderManager.IsDisposed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DrawableWithDependencies : CompositeDrawable
|
private class DrawableWithDependencies : CompositeDrawable
|
||||||
{
|
{
|
||||||
public TestTextureStore ParentTextureStore { get; private set; }
|
public TestTextureStore ParentTextureStore { get; private set; }
|
||||||
public TestSampleStore ParentSampleStore { get; private set; }
|
public TestSampleStore ParentSampleStore { get; private set; }
|
||||||
|
public TestShaderManager ParentShaderManager { get; private set; }
|
||||||
|
|
||||||
public DrawableWithDependencies()
|
public DrawableWithDependencies()
|
||||||
{
|
{
|
||||||
@ -70,6 +76,7 @@ namespace osu.Game.Tests.Rulesets
|
|||||||
|
|
||||||
dependencies.CacheAs<TextureStore>(ParentTextureStore = new TestTextureStore());
|
dependencies.CacheAs<TextureStore>(ParentTextureStore = new TestTextureStore());
|
||||||
dependencies.CacheAs<ISampleStore>(ParentSampleStore = new TestSampleStore());
|
dependencies.CacheAs<ISampleStore>(ParentSampleStore = new TestSampleStore());
|
||||||
|
dependencies.CacheAs<ShaderManager>(ParentShaderManager = new TestShaderManager());
|
||||||
|
|
||||||
return new DrawableRulesetDependencies(new OsuRuleset(), dependencies);
|
return new DrawableRulesetDependencies(new OsuRuleset(), dependencies);
|
||||||
}
|
}
|
||||||
@ -135,5 +142,23 @@ namespace osu.Game.Tests.Rulesets
|
|||||||
|
|
||||||
public int PlaybackConcurrency { get; set; }
|
public int PlaybackConcurrency { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestShaderManager : ShaderManager
|
||||||
|
{
|
||||||
|
public TestShaderManager()
|
||||||
|
: base(new ResourceStore<byte[]>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] LoadRaw(string name) => null;
|
||||||
|
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
IsDisposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
94
osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs
Normal file
94
osu.Game.Tests/Skins/TestSceneSkinProvidingContainer.cs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.OpenGL.Textures;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Skins
|
||||||
|
{
|
||||||
|
[HeadlessTest]
|
||||||
|
public class TestSceneSkinProvidingContainer : OsuTestScene
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that the first inserted skin after resetting (via source change)
|
||||||
|
/// is always prioritised over others when providing the same resource.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPriorityPreservation()
|
||||||
|
{
|
||||||
|
TestSkinProvidingContainer provider = null;
|
||||||
|
TestSkin mostPrioritisedSource = null;
|
||||||
|
|
||||||
|
AddStep("setup sources", () =>
|
||||||
|
{
|
||||||
|
var sources = new List<TestSkin>();
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
sources.Add(new TestSkin());
|
||||||
|
|
||||||
|
mostPrioritisedSource = sources.First();
|
||||||
|
|
||||||
|
Child = provider = new TestSkinProvidingContainer(sources);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("texture provided by expected skin", () =>
|
||||||
|
{
|
||||||
|
return provider.FindProvider(s => s.GetTexture(TestSkin.TEXTURE_NAME) != null) == mostPrioritisedSource;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("trigger source change", () => provider.TriggerSourceChanged());
|
||||||
|
|
||||||
|
AddAssert("texture still provided by expected skin", () =>
|
||||||
|
{
|
||||||
|
return provider.FindProvider(s => s.GetTexture(TestSkin.TEXTURE_NAME) != null) == mostPrioritisedSource;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestSkinProvidingContainer : SkinProvidingContainer
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<ISkin> sources;
|
||||||
|
|
||||||
|
public TestSkinProvidingContainer(IEnumerable<ISkin> sources)
|
||||||
|
{
|
||||||
|
this.sources = sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void TriggerSourceChanged() => base.TriggerSourceChanged();
|
||||||
|
|
||||||
|
protected override void OnSourceChanged()
|
||||||
|
{
|
||||||
|
ResetSources();
|
||||||
|
sources.ForEach(AddSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestSkin : ISkin
|
||||||
|
{
|
||||||
|
public const string TEXTURE_NAME = "virtual-texture";
|
||||||
|
|
||||||
|
public Drawable GetDrawableComponent(ISkinComponent component) => throw new System.NotImplementedException();
|
||||||
|
|
||||||
|
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT)
|
||||||
|
{
|
||||||
|
if (componentName == TEXTURE_NAME)
|
||||||
|
return Texture.WhitePixel;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISample GetSample(ISampleInfo sampleInfo) => throw new System.NotImplementedException();
|
||||||
|
|
||||||
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Configuration.Tracking;
|
using osu.Framework.Configuration.Tracking;
|
||||||
|
using osu.Framework.Graphics.Shaders;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
@ -45,6 +46,14 @@ namespace osu.Game.Tests.Testing
|
|||||||
Dependencies.Get<ISampleStore>().Get(@"test-sample") != null);
|
Dependencies.Get<ISampleStore>().Get(@"test-sample") != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRetrieveShader()
|
||||||
|
{
|
||||||
|
AddAssert("ruleset shaders retrieved", () =>
|
||||||
|
Dependencies.Get<ShaderManager>().LoadRaw(@"sh_TestVertex.vs") != null &&
|
||||||
|
Dependencies.Get<ShaderManager>().LoadRaw(@"sh_TestFragment.fs") != null);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestResolveConfigManager()
|
public void TestResolveConfigManager()
|
||||||
{
|
{
|
||||||
|
@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
showOverlay();
|
showOverlay();
|
||||||
|
|
||||||
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
||||||
AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected.Value);
|
AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
showOverlay();
|
showOverlay();
|
||||||
|
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddAssert("First button selected", () => getButton(0).Selected.Value);
|
AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -111,11 +111,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("Show overlay", () => failOverlay.Show());
|
AddStep("Show overlay", () => failOverlay.Show());
|
||||||
|
|
||||||
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
||||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value);
|
AddAssert("Last button selected", () => failOverlay.Buttons.Last().State == SelectionState.Selected);
|
||||||
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
||||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value);
|
AddAssert("First button selected", () => failOverlay.Buttons.First().State == SelectionState.Selected);
|
||||||
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
||||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value);
|
AddAssert("Last button selected", () => failOverlay.Buttons.Last().State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -127,11 +127,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("Show overlay", () => failOverlay.Show());
|
AddStep("Show overlay", () => failOverlay.Show());
|
||||||
|
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value);
|
AddAssert("First button selected", () => failOverlay.Buttons.First().State == SelectionState.Selected);
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected.Value);
|
AddAssert("Last button selected", () => failOverlay.Buttons.Last().State == SelectionState.Selected);
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected.Value);
|
AddAssert("First button selected", () => failOverlay.Buttons.First().State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First()));
|
AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First()));
|
||||||
AddStep("Hide overlay", () => failOverlay.Hide());
|
AddStep("Hide overlay", () => failOverlay.Hide());
|
||||||
|
|
||||||
AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected.Value));
|
AddAssert("Overlay state is reset", () => failOverlay.Buttons.All(b => b.State == SelectionState.NotSelected));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -162,11 +162,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||||
showOverlay();
|
showOverlay();
|
||||||
|
|
||||||
AddAssert("First button not selected", () => !getButton(0).Selected.Value);
|
AddAssert("First button not selected", () => getButton(0).State == SelectionState.NotSelected);
|
||||||
|
|
||||||
AddStep("Move slightly", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(1)));
|
AddStep("Move slightly", () => InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(1)));
|
||||||
|
|
||||||
AddAssert("First button selected", () => getButton(0).Selected.Value);
|
AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -179,8 +179,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
||||||
AddAssert("First button not selected", () => !getButton(0).Selected.Value);
|
AddAssert("First button not selected", () => getButton(0).State == SelectionState.NotSelected);
|
||||||
AddAssert("Second button selected", () => getButton(1).Selected.Value);
|
AddAssert("Second button selected", () => getButton(1).State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -196,8 +196,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
||||||
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
AddStep("Up arrow", () => InputManager.Key(Key.Up));
|
||||||
AddAssert("Second button not selected", () => !getButton(1).Selected.Value);
|
AddAssert("Second button not selected", () => getButton(1).State == SelectionState.NotSelected);
|
||||||
AddAssert("First button selected", () => getButton(0).Selected.Value);
|
AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -211,7 +211,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
AddStep("Hover second button", () => InputManager.MoveMouseTo(getButton(1)));
|
||||||
AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero));
|
AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero));
|
||||||
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
AddStep("Down arrow", () => InputManager.Key(Key.Down));
|
||||||
AddAssert("First button selected", () => getButton(0).Selected.Value); // Initial state condition
|
AddAssert("First button selected", () => getButton(0).State == SelectionState.Selected); // Initial state condition
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -282,7 +282,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
showOverlay();
|
showOverlay();
|
||||||
|
|
||||||
AddAssert("No button selected",
|
AddAssert("No button selected",
|
||||||
() => pauseOverlay.Buttons.All(button => !button.Selected.Value));
|
() => pauseOverlay.Buttons.All(button => button.State == SelectionState.NotSelected));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showOverlay() => AddStep("Show overlay", () => pauseOverlay.Show());
|
private void showOverlay() => AddStep("Show overlay", () => pauseOverlay.Show());
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Online.Solo;
|
using osu.Game.Online.Solo;
|
||||||
@ -12,6 +13,7 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
@ -25,6 +27,15 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
protected override bool HasCustomSteps => true;
|
protected override bool HasCustomSteps => true;
|
||||||
|
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||||
|
{
|
||||||
|
var beatmap = (TestBeatmap)base.CreateBeatmap(ruleset);
|
||||||
|
|
||||||
|
beatmap.HitObjects = beatmap.HitObjects.Take(10).ToList();
|
||||||
|
|
||||||
|
return beatmap;
|
||||||
|
}
|
||||||
|
|
||||||
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false);
|
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false);
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -168,7 +168,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public void Disable()
|
public void Disable()
|
||||||
{
|
{
|
||||||
allow = false;
|
allow = false;
|
||||||
OnSourceChanged();
|
TriggerSourceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwitchableSkinProvidingContainer(ISkin skin)
|
public SwitchableSkinProvidingContainer(ISkin skin)
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Tests.Visual.Settings
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Add(new DirectorySelector { RelativeSizeAxes = Axes.Both });
|
Add(new OsuDirectorySelector { RelativeSizeAxes = Axes.Both });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,13 @@ namespace osu.Game.Tests.Visual.Settings
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestAllFiles()
|
public void TestAllFiles()
|
||||||
{
|
{
|
||||||
AddStep("create", () => Child = new FileSelector { RelativeSizeAxes = Axes.Both });
|
AddStep("create", () => Child = new OsuFileSelector { RelativeSizeAxes = Axes.Both });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestJpgFilesOnly()
|
public void TestJpgFilesOnly()
|
||||||
{
|
{
|
||||||
AddStep("create", () => Child = new FileSelector(validFileExtensions: new[] { ".jpg" }) { RelativeSizeAxes = Axes.Both });
|
AddStep("create", () => Child = new OsuFileSelector(validFileExtensions: new[] { ".jpg" }) { RelativeSizeAxes = Axes.Both });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Volume;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
public class TestSceneVolumeOverlay : OsuTestScene
|
||||||
|
{
|
||||||
|
private VolumeOverlay volume;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
volume = new VolumeOverlay(),
|
||||||
|
new VolumeControlReceptor
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ActionRequested = action => volume.Adjust(action),
|
||||||
|
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("show controls", () => volume.Show());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,7 @@ namespace osu.Game.Tournament.Screens.Setup
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private MatchIPCInfo ipc { get; set; }
|
private MatchIPCInfo ipc { get; set; }
|
||||||
|
|
||||||
private DirectorySelector directorySelector;
|
private OsuDirectorySelector directorySelector;
|
||||||
private DialogOverlay overlay;
|
private DialogOverlay overlay;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
@ -79,7 +79,7 @@ namespace osu.Game.Tournament.Screens.Setup
|
|||||||
},
|
},
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
directorySelector = new DirectorySelector(initialPath)
|
directorySelector = new OsuDirectorySelector(initialPath)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
// 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.Diagnostics;
|
||||||
|
using osu.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.Containers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A FillFlowContainer that provides functionality to cycle selection between children
|
||||||
|
/// The selection wraps around when overflowing past the first or last child.
|
||||||
|
/// </summary>
|
||||||
|
public class SelectionCycleFillFlowContainer<T> : FillFlowContainer<T> where T : Drawable, IStateful<SelectionState>
|
||||||
|
{
|
||||||
|
public T Selected => (selectedIndex >= 0 && selectedIndex < Count) ? this[selectedIndex.Value] : null;
|
||||||
|
|
||||||
|
private int? selectedIndex;
|
||||||
|
|
||||||
|
public void SelectNext()
|
||||||
|
{
|
||||||
|
if (!selectedIndex.HasValue || selectedIndex == Count - 1)
|
||||||
|
setSelected(0);
|
||||||
|
else
|
||||||
|
setSelected(selectedIndex.Value + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SelectPrevious()
|
||||||
|
{
|
||||||
|
if (!selectedIndex.HasValue || selectedIndex == 0)
|
||||||
|
setSelected(Count - 1);
|
||||||
|
else
|
||||||
|
setSelected(selectedIndex.Value - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deselect() => setSelected(null);
|
||||||
|
|
||||||
|
public void Select(T item)
|
||||||
|
{
|
||||||
|
var newIndex = IndexOf(item);
|
||||||
|
|
||||||
|
if (newIndex < 0)
|
||||||
|
setSelected(null);
|
||||||
|
else
|
||||||
|
setSelected(IndexOf(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Add(T drawable)
|
||||||
|
{
|
||||||
|
base.Add(drawable);
|
||||||
|
|
||||||
|
Debug.Assert(drawable != null);
|
||||||
|
|
||||||
|
drawable.StateChanged += state => selectionChanged(drawable, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Remove(T drawable)
|
||||||
|
=> throw new NotSupportedException($"Cannot remove drawables from {nameof(SelectionCycleFillFlowContainer<T>)}");
|
||||||
|
|
||||||
|
private void setSelected(int? value)
|
||||||
|
{
|
||||||
|
if (selectedIndex == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Deselect the previously-selected button
|
||||||
|
if (selectedIndex.HasValue)
|
||||||
|
this[selectedIndex.Value].State = SelectionState.NotSelected;
|
||||||
|
|
||||||
|
selectedIndex = value;
|
||||||
|
|
||||||
|
// Select the newly-selected button
|
||||||
|
if (selectedIndex.HasValue)
|
||||||
|
this[selectedIndex.Value].State = SelectionState.Selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectionChanged(T drawable, SelectionState state)
|
||||||
|
{
|
||||||
|
if (state == SelectionState.NotSelected)
|
||||||
|
Deselect();
|
||||||
|
else
|
||||||
|
Select(drawable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,26 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Bindables;
|
using System;
|
||||||
using osuTK;
|
using osu.Framework;
|
||||||
using osuTK.Graphics;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Graphics.Backgrounds;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics.Effects;
|
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Graphics.Backgrounds;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class DialogButton : OsuClickableContainer
|
public class DialogButton : OsuClickableContainer, IStateful<SelectionState>
|
||||||
{
|
{
|
||||||
private const float idle_width = 0.8f;
|
private const float idle_width = 0.8f;
|
||||||
private const float hover_width = 0.9f;
|
private const float hover_width = 0.9f;
|
||||||
@ -27,7 +28,22 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
private const float hover_duration = 500;
|
private const float hover_duration = 500;
|
||||||
private const float click_duration = 200;
|
private const float click_duration = 200;
|
||||||
|
|
||||||
public readonly BindableBool Selected = new BindableBool();
|
public event Action<SelectionState> StateChanged;
|
||||||
|
|
||||||
|
private SelectionState state;
|
||||||
|
|
||||||
|
public SelectionState State
|
||||||
|
{
|
||||||
|
get => state;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (state == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state = value;
|
||||||
|
StateChanged?.Invoke(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly Container backgroundContainer;
|
private readonly Container backgroundContainer;
|
||||||
private readonly Container colourContainer;
|
private readonly Container colourContainer;
|
||||||
@ -153,7 +169,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
updateGlow();
|
updateGlow();
|
||||||
|
|
||||||
Selected.ValueChanged += selectionChanged;
|
StateChanged += selectionChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 buttonColour;
|
private Color4 buttonColour;
|
||||||
@ -221,7 +237,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
.OnComplete(_ =>
|
.OnComplete(_ =>
|
||||||
{
|
{
|
||||||
clickAnimating = false;
|
clickAnimating = false;
|
||||||
Selected.TriggerChange();
|
StateChanged?.Invoke(State);
|
||||||
});
|
});
|
||||||
|
|
||||||
return base.OnClick(e);
|
return base.OnClick(e);
|
||||||
@ -235,7 +251,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
protected override void OnMouseUp(MouseUpEvent e)
|
protected override void OnMouseUp(MouseUpEvent e)
|
||||||
{
|
{
|
||||||
if (Selected.Value)
|
if (State == SelectionState.Selected)
|
||||||
colourContainer.ResizeWidthTo(hover_width, click_duration, Easing.In);
|
colourContainer.ResizeWidthTo(hover_width, click_duration, Easing.In);
|
||||||
base.OnMouseUp(e);
|
base.OnMouseUp(e);
|
||||||
}
|
}
|
||||||
@ -243,7 +259,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
base.OnHover(e);
|
base.OnHover(e);
|
||||||
Selected.Value = true;
|
State = SelectionState.Selected;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -251,15 +267,15 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
protected override void OnHoverLost(HoverLostEvent e)
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
{
|
{
|
||||||
base.OnHoverLost(e);
|
base.OnHoverLost(e);
|
||||||
Selected.Value = false;
|
State = SelectionState.NotSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectionChanged(ValueChangedEvent<bool> args)
|
private void selectionChanged(SelectionState newState)
|
||||||
{
|
{
|
||||||
if (clickAnimating)
|
if (clickAnimating)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (args.NewValue)
|
if (newState == SelectionState.Selected)
|
||||||
{
|
{
|
||||||
spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic);
|
spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic);
|
||||||
colourContainer.ResizeWidthTo(hover_width, hover_duration, Easing.OutElastic);
|
colourContainer.ResizeWidthTo(hover_width, hover_duration, Easing.OutElastic);
|
||||||
|
@ -1,297 +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.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Framework.Platform;
|
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterfaceV2
|
|
||||||
{
|
|
||||||
public class DirectorySelector : CompositeDrawable
|
|
||||||
{
|
|
||||||
private FillFlowContainer directoryFlow;
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private GameHost host { get; set; }
|
|
||||||
|
|
||||||
[Cached]
|
|
||||||
public readonly Bindable<DirectoryInfo> CurrentPath = new Bindable<DirectoryInfo>();
|
|
||||||
|
|
||||||
public DirectorySelector(string initialPath = null)
|
|
||||||
{
|
|
||||||
CurrentPath.Value = new DirectoryInfo(initialPath ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
Padding = new MarginPadding(10);
|
|
||||||
|
|
||||||
InternalChild = new GridContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
RowDimensions = new[]
|
|
||||||
{
|
|
||||||
new Dimension(GridSizeMode.Absolute, 50),
|
|
||||||
new Dimension(),
|
|
||||||
},
|
|
||||||
Content = new[]
|
|
||||||
{
|
|
||||||
new Drawable[]
|
|
||||||
{
|
|
||||||
new CurrentDirectoryDisplay
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new Drawable[]
|
|
||||||
{
|
|
||||||
new OsuScrollContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Child = directoryFlow = new FillFlowContainer
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Spacing = new Vector2(2),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
CurrentPath.BindValueChanged(updateDisplay, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateDisplay(ValueChangedEvent<DirectoryInfo> directory)
|
|
||||||
{
|
|
||||||
directoryFlow.Clear();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (directory.NewValue == null)
|
|
||||||
{
|
|
||||||
var drives = DriveInfo.GetDrives();
|
|
||||||
|
|
||||||
foreach (var drive in drives)
|
|
||||||
directoryFlow.Add(new DirectoryPiece(drive.RootDirectory));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
directoryFlow.Add(new ParentDirectoryPiece(CurrentPath.Value.Parent));
|
|
||||||
|
|
||||||
directoryFlow.AddRange(GetEntriesForPath(CurrentPath.Value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
CurrentPath.Value = directory.OldValue;
|
|
||||||
this.FlashColour(Color4.Red, 300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual IEnumerable<DisplayPiece> GetEntriesForPath(DirectoryInfo path)
|
|
||||||
{
|
|
||||||
foreach (var dir in path.GetDirectories().OrderBy(d => d.Name))
|
|
||||||
{
|
|
||||||
if ((dir.Attributes & FileAttributes.Hidden) == 0)
|
|
||||||
yield return new DirectoryPiece(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CurrentDirectoryDisplay : CompositeDrawable
|
|
||||||
{
|
|
||||||
[Resolved]
|
|
||||||
private Bindable<DirectoryInfo> currentDirectory { get; set; }
|
|
||||||
|
|
||||||
private FillFlowContainer flow;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
|
||||||
flow = new FillFlowContainer
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Spacing = new Vector2(5),
|
|
||||||
Height = DisplayPiece.HEIGHT,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
currentDirectory.BindValueChanged(updateDisplay, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateDisplay(ValueChangedEvent<DirectoryInfo> dir)
|
|
||||||
{
|
|
||||||
flow.Clear();
|
|
||||||
|
|
||||||
List<DirectoryPiece> pathPieces = new List<DirectoryPiece>();
|
|
||||||
|
|
||||||
DirectoryInfo ptr = dir.NewValue;
|
|
||||||
|
|
||||||
while (ptr != null)
|
|
||||||
{
|
|
||||||
pathPieces.Insert(0, new CurrentDisplayPiece(ptr));
|
|
||||||
ptr = ptr.Parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
flow.ChildrenEnumerable = new Drawable[]
|
|
||||||
{
|
|
||||||
new OsuSpriteText { Text = "Current Directory: ", Font = OsuFont.Default.With(size: DisplayPiece.HEIGHT), },
|
|
||||||
new ComputerPiece(),
|
|
||||||
}.Concat(pathPieces);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ComputerPiece : CurrentDisplayPiece
|
|
||||||
{
|
|
||||||
protected override IconUsage? Icon => null;
|
|
||||||
|
|
||||||
public ComputerPiece()
|
|
||||||
: base(null, "Computer")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CurrentDisplayPiece : DirectoryPiece
|
|
||||||
{
|
|
||||||
public CurrentDisplayPiece(DirectoryInfo directory, string displayName = null)
|
|
||||||
: base(directory, displayName)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
Flow.Add(new SpriteIcon
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Icon = FontAwesome.Solid.ChevronRight,
|
|
||||||
Size = new Vector2(FONT_SIZE / 2)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) ? base.Icon : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ParentDirectoryPiece : DirectoryPiece
|
|
||||||
{
|
|
||||||
protected override IconUsage? Icon => FontAwesome.Solid.Folder;
|
|
||||||
|
|
||||||
public ParentDirectoryPiece(DirectoryInfo directory)
|
|
||||||
: base(directory, "..")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class DirectoryPiece : DisplayPiece
|
|
||||||
{
|
|
||||||
protected readonly DirectoryInfo Directory;
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private Bindable<DirectoryInfo> currentDirectory { get; set; }
|
|
||||||
|
|
||||||
public DirectoryPiece(DirectoryInfo directory, string displayName = null)
|
|
||||||
: base(displayName)
|
|
||||||
{
|
|
||||||
Directory = directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
|
||||||
{
|
|
||||||
currentDirectory.Value = Directory;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override string FallbackName => Directory.Name;
|
|
||||||
|
|
||||||
protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar)
|
|
||||||
? FontAwesome.Solid.Database
|
|
||||||
: FontAwesome.Regular.Folder;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract class DisplayPiece : CompositeDrawable
|
|
||||||
{
|
|
||||||
public const float HEIGHT = 20;
|
|
||||||
|
|
||||||
protected const float FONT_SIZE = 16;
|
|
||||||
|
|
||||||
private readonly string displayName;
|
|
||||||
|
|
||||||
protected FillFlowContainer Flow;
|
|
||||||
|
|
||||||
protected DisplayPiece(string displayName = null)
|
|
||||||
{
|
|
||||||
this.displayName = displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
Masking = true;
|
|
||||||
CornerRadius = 5;
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Colour = colours.GreySeafoamDarker,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
Flow = new FillFlowContainer
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.X,
|
|
||||||
Height = 20,
|
|
||||||
Margin = new MarginPadding { Vertical = 2, Horizontal = 5 },
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Spacing = new Vector2(5),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Icon.HasValue)
|
|
||||||
{
|
|
||||||
Flow.Add(new SpriteIcon
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Icon = Icon.Value,
|
|
||||||
Size = new Vector2(FONT_SIZE)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Flow.Add(new OsuSpriteText
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Text = displayName ?? FallbackName,
|
|
||||||
Font = OsuFont.Default.With(size: FONT_SIZE)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract string FallbackName { get; }
|
|
||||||
|
|
||||||
protected abstract IconUsage? Icon { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,94 +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.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterfaceV2
|
|
||||||
{
|
|
||||||
public class FileSelector : DirectorySelector
|
|
||||||
{
|
|
||||||
private readonly string[] validFileExtensions;
|
|
||||||
|
|
||||||
[Cached]
|
|
||||||
public readonly Bindable<FileInfo> CurrentFile = new Bindable<FileInfo>();
|
|
||||||
|
|
||||||
public FileSelector(string initialPath = null, string[] validFileExtensions = null)
|
|
||||||
: base(initialPath)
|
|
||||||
{
|
|
||||||
this.validFileExtensions = validFileExtensions ?? Array.Empty<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override IEnumerable<DisplayPiece> GetEntriesForPath(DirectoryInfo path)
|
|
||||||
{
|
|
||||||
foreach (var dir in base.GetEntriesForPath(path))
|
|
||||||
yield return dir;
|
|
||||||
|
|
||||||
IEnumerable<FileInfo> files = path.GetFiles();
|
|
||||||
|
|
||||||
if (validFileExtensions.Length > 0)
|
|
||||||
files = files.Where(f => validFileExtensions.Contains(f.Extension));
|
|
||||||
|
|
||||||
foreach (var file in files.OrderBy(d => d.Name))
|
|
||||||
{
|
|
||||||
if ((file.Attributes & FileAttributes.Hidden) == 0)
|
|
||||||
yield return new FilePiece(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class FilePiece : DisplayPiece
|
|
||||||
{
|
|
||||||
private readonly FileInfo file;
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private Bindable<FileInfo> currentFile { get; set; }
|
|
||||||
|
|
||||||
public FilePiece(FileInfo file)
|
|
||||||
{
|
|
||||||
this.file = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
|
||||||
{
|
|
||||||
currentFile.Value = file;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override string FallbackName => file.Name;
|
|
||||||
|
|
||||||
protected override IconUsage? Icon
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
switch (file.Extension)
|
|
||||||
{
|
|
||||||
case ".ogg":
|
|
||||||
case ".mp3":
|
|
||||||
case ".wav":
|
|
||||||
return FontAwesome.Regular.FileAudio;
|
|
||||||
|
|
||||||
case ".jpg":
|
|
||||||
case ".jpeg":
|
|
||||||
case ".png":
|
|
||||||
return FontAwesome.Regular.FileImage;
|
|
||||||
|
|
||||||
case ".mp4":
|
|
||||||
case ".avi":
|
|
||||||
case ".mov":
|
|
||||||
case ".flv":
|
|
||||||
return FontAwesome.Regular.FileVideo;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return FontAwesome.Regular.File;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
38
osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.cs
Normal file
38
osu.Game/Graphics/UserInterfaceV2/OsuDirectorySelector.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.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
|
{
|
||||||
|
public class OsuDirectorySelector : DirectorySelector
|
||||||
|
{
|
||||||
|
public const float ITEM_HEIGHT = 20;
|
||||||
|
|
||||||
|
public OsuDirectorySelector(string initialPath = null)
|
||||||
|
: base(initialPath)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ScrollContainer<Drawable> CreateScrollContainer() => new OsuScrollContainer();
|
||||||
|
|
||||||
|
protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay();
|
||||||
|
|
||||||
|
protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory);
|
||||||
|
|
||||||
|
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName);
|
||||||
|
|
||||||
|
protected override void NotifySelectionError() => this.FlashColour(Colour4.Red, 300);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
|
{
|
||||||
|
internal class OsuDirectorySelectorBreadcrumbDisplay : DirectorySelectorBreadcrumbDisplay
|
||||||
|
{
|
||||||
|
protected override Drawable CreateCaption() => new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = "Current Directory: ",
|
||||||
|
Font = OsuFont.Default.With(size: OsuDirectorySelector.ITEM_HEIGHT),
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override DirectorySelectorDirectory CreateRootDirectoryItem() => new OsuBreadcrumbDisplayComputer();
|
||||||
|
|
||||||
|
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuBreadcrumbDisplayDirectory(directory, displayName);
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Height = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OsuBreadcrumbDisplayComputer : OsuBreadcrumbDisplayDirectory
|
||||||
|
{
|
||||||
|
protected override IconUsage? Icon => null;
|
||||||
|
|
||||||
|
public OsuBreadcrumbDisplayComputer()
|
||||||
|
: base(null, "Computer")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OsuBreadcrumbDisplayDirectory : OsuDirectorySelectorDirectory
|
||||||
|
{
|
||||||
|
public OsuBreadcrumbDisplayDirectory(DirectoryInfo directory, string displayName = null)
|
||||||
|
: base(directory, displayName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Flow.Add(new SpriteIcon
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Icon = FontAwesome.Solid.ChevronRight,
|
||||||
|
Size = new Vector2(FONT_SIZE / 2)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) ? base.Icon : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.IO;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
|
{
|
||||||
|
internal class OsuDirectorySelectorDirectory : DirectorySelectorDirectory
|
||||||
|
{
|
||||||
|
public OsuDirectorySelectorDirectory(DirectoryInfo directory, string displayName = null)
|
||||||
|
: base(directory, displayName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Flow.AutoSizeAxes = Axes.X;
|
||||||
|
Flow.Height = OsuDirectorySelector.ITEM_HEIGHT;
|
||||||
|
|
||||||
|
AddInternal(new Background
|
||||||
|
{
|
||||||
|
Depth = 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override SpriteText CreateSpriteText() => new OsuSpriteText();
|
||||||
|
|
||||||
|
protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar)
|
||||||
|
? FontAwesome.Solid.Database
|
||||||
|
: FontAwesome.Regular.Folder;
|
||||||
|
|
||||||
|
internal class Background : CompositeDrawable
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 5;
|
||||||
|
|
||||||
|
InternalChild = new Box
|
||||||
|
{
|
||||||
|
Colour = colours.GreySeafoamDarker,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
|
{
|
||||||
|
internal class OsuDirectorySelectorParentDirectory : OsuDirectorySelectorDirectory
|
||||||
|
{
|
||||||
|
protected override IconUsage? Icon => FontAwesome.Solid.Folder;
|
||||||
|
|
||||||
|
public OsuDirectorySelectorParentDirectory(DirectoryInfo directory)
|
||||||
|
: base(directory, "..")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs
Normal file
90
osu.Game/Graphics/UserInterfaceV2/OsuFileSelector.cs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
|
{
|
||||||
|
public class OsuFileSelector : FileSelector
|
||||||
|
{
|
||||||
|
public OsuFileSelector(string initialPath = null, string[] validFileExtensions = null)
|
||||||
|
: base(initialPath, validFileExtensions)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ScrollContainer<Drawable> CreateScrollContainer() => new OsuScrollContainer();
|
||||||
|
|
||||||
|
protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay();
|
||||||
|
|
||||||
|
protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory);
|
||||||
|
|
||||||
|
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName);
|
||||||
|
|
||||||
|
protected override DirectoryListingFile CreateFileItem(FileInfo file) => new OsuDirectoryListingFile(file);
|
||||||
|
|
||||||
|
protected override void NotifySelectionError() => this.FlashColour(Colour4.Red, 300);
|
||||||
|
|
||||||
|
protected class OsuDirectoryListingFile : DirectoryListingFile
|
||||||
|
{
|
||||||
|
public OsuDirectoryListingFile(FileInfo file)
|
||||||
|
: base(file)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Flow.AutoSizeAxes = Axes.X;
|
||||||
|
Flow.Height = OsuDirectorySelector.ITEM_HEIGHT;
|
||||||
|
|
||||||
|
AddInternal(new OsuDirectorySelectorDirectory.Background
|
||||||
|
{
|
||||||
|
Depth = 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IconUsage? Icon
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (File.Extension)
|
||||||
|
{
|
||||||
|
case @".ogg":
|
||||||
|
case @".mp3":
|
||||||
|
case @".wav":
|
||||||
|
return FontAwesome.Regular.FileAudio;
|
||||||
|
|
||||||
|
case @".jpg":
|
||||||
|
case @".jpeg":
|
||||||
|
case @".png":
|
||||||
|
return FontAwesome.Regular.FileImage;
|
||||||
|
|
||||||
|
case @".mp4":
|
||||||
|
case @".avi":
|
||||||
|
case @".mov":
|
||||||
|
case @".flv":
|
||||||
|
return FontAwesome.Regular.FileVideo;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return FontAwesome.Regular.File;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override SpriteText CreateSpriteText() => new OsuSpriteText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -103,6 +103,9 @@ namespace osu.Game.Input.Bindings
|
|||||||
new KeyBinding(new[] { InputKey.Alt, InputKey.Up }, GlobalAction.IncreaseVolume),
|
new KeyBinding(new[] { InputKey.Alt, InputKey.Up }, GlobalAction.IncreaseVolume),
|
||||||
new KeyBinding(new[] { InputKey.Alt, InputKey.Down }, GlobalAction.DecreaseVolume),
|
new KeyBinding(new[] { InputKey.Alt, InputKey.Down }, GlobalAction.DecreaseVolume),
|
||||||
|
|
||||||
|
new KeyBinding(new[] { InputKey.Alt, InputKey.Left }, GlobalAction.PreviousVolumeMeter),
|
||||||
|
new KeyBinding(new[] { InputKey.Alt, InputKey.Right }, GlobalAction.NextVolumeMeter),
|
||||||
|
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.F4 }, GlobalAction.ToggleMute),
|
new KeyBinding(new[] { InputKey.Control, InputKey.F4 }, GlobalAction.ToggleMute),
|
||||||
|
|
||||||
new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev),
|
new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev),
|
||||||
@ -263,5 +266,11 @@ namespace osu.Game.Input.Bindings
|
|||||||
|
|
||||||
[Description("Toggle skin editor")]
|
[Description("Toggle skin editor")]
|
||||||
ToggleSkinEditor,
|
ToggleSkinEditor,
|
||||||
|
|
||||||
|
[Description("Previous volume meter")]
|
||||||
|
PreviousVolumeMeter,
|
||||||
|
|
||||||
|
[Description("Next volume meter")]
|
||||||
|
NextVolumeMeter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
public class CountryFilter : CompositeDrawable, IHasCurrentValue<Country>
|
public class CountryFilter : CompositeDrawable, IHasCurrentValue<Country>
|
||||||
{
|
{
|
||||||
private const int duration = 200;
|
private const int duration = 200;
|
||||||
private const int height = 50;
|
private const int height = 70;
|
||||||
|
|
||||||
private readonly BindableWithCurrent<Country> current = new BindableWithCurrent<Country>();
|
private readonly BindableWithCurrent<Country> current = new BindableWithCurrent<Country>();
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
|
|
||||||
InternalChild = content = new CircularContainer
|
InternalChild = content = new CircularContainer
|
||||||
{
|
{
|
||||||
Height = 25,
|
Height = 30,
|
||||||
AutoSizeDuration = duration,
|
AutoSizeDuration = duration,
|
||||||
AutoSizeEasing = Easing.OutQuint,
|
AutoSizeEasing = Easing.OutQuint,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
@ -58,9 +58,9 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
Margin = new MarginPadding { Horizontal = 10 },
|
Margin = new MarginPadding { Horizontal = 15 },
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(8, 0),
|
Spacing = new Vector2(15, 0),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
@ -70,14 +70,14 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(3, 0),
|
Spacing = new Vector2(5, 0),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
flag = new UpdateableFlag
|
flag = new UpdateableFlag
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Size = new Vector2(22, 15)
|
Size = new Vector2(30, 20)
|
||||||
},
|
},
|
||||||
countryName = new OsuSpriteText
|
countryName = new OsuSpriteText
|
||||||
{
|
{
|
||||||
@ -148,7 +148,7 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Add(icon = new SpriteIcon
|
Add(icon = new SpriteIcon
|
||||||
{
|
{
|
||||||
Size = new Vector2(8),
|
Size = new Vector2(10),
|
||||||
Icon = FontAwesome.Solid.Times
|
Icon = FontAwesome.Solid.Times
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Rankings
|
namespace osu.Game.Overlays.Rankings
|
||||||
{
|
{
|
||||||
@ -46,6 +47,7 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
background = new Box
|
background = new Box
|
||||||
@ -139,7 +141,7 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Direction = FillDirection.Vertical;
|
Direction = FillDirection.Vertical;
|
||||||
Margin = new MarginPadding { Vertical = 10 };
|
Padding = new MarginPadding { Vertical = 15 };
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
@ -150,11 +152,11 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
Height = 20,
|
Height = 25,
|
||||||
Child = valueText = new OsuSpriteText
|
Child = valueText = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Light),
|
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Light),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,11 +176,34 @@ namespace osu.Game.Overlays.Rankings
|
|||||||
|
|
||||||
protected override DropdownMenu CreateMenu() => menu = base.CreateMenu().With(m => m.MaxHeight = 400);
|
protected override DropdownMenu CreateMenu() => menu = base.CreateMenu().With(m => m.MaxHeight = 400);
|
||||||
|
|
||||||
|
protected override DropdownHeader CreateHeader() => new SpotlightsDropdownHeader();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OverlayColourProvider colourProvider)
|
private void load(OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
|
// osu-web adds a 0.6 opacity container on top of the 0.5 base one when hovering, 0.8 on a single container here matches the resulting colour
|
||||||
|
AccentColour = colourProvider.Background6.Opacity(0.8f);
|
||||||
menu.BackgroundColour = colourProvider.Background5;
|
menu.BackgroundColour = colourProvider.Background5;
|
||||||
AccentColour = colourProvider.Background6;
|
Padding = new MarginPadding { Vertical = 20 };
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SpotlightsDropdownHeader : OsuDropdownHeader
|
||||||
|
{
|
||||||
|
public SpotlightsDropdownHeader()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
Text.Font = OsuFont.GetFont(size: 15);
|
||||||
|
Text.Padding = new MarginPadding { Vertical = 1.5f }; // osu-web line-height difference compensation
|
||||||
|
Foreground.Padding = new MarginPadding { Horizontal = 10, Vertical = 15 };
|
||||||
|
Margin = Icon.Margin = new MarginPadding(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
BackgroundColour = colourProvider.Background6.Opacity(0.5f);
|
||||||
|
BackgroundColourHover = colourProvider.Background5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Game.Graphics.Containers;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Rankings.Tables
|
namespace osu.Game.Overlays.Rankings.Tables
|
||||||
{
|
{
|
||||||
@ -62,35 +61,20 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private class CountryName : OsuHoverContainer
|
private class CountryName : LinkFlowContainer
|
||||||
{
|
{
|
||||||
protected override IEnumerable<Drawable> EffectTargets => new[] { text };
|
|
||||||
|
|
||||||
[Resolved(canBeNull: true)]
|
[Resolved(canBeNull: true)]
|
||||||
private RankingsOverlay rankings { get; set; }
|
private RankingsOverlay rankings { get; set; }
|
||||||
|
|
||||||
private readonly OsuSpriteText text;
|
|
||||||
private readonly Country country;
|
|
||||||
|
|
||||||
public CountryName(Country country)
|
public CountryName(Country country)
|
||||||
|
: base(t => t.Font = OsuFont.GetFont(size: 12))
|
||||||
{
|
{
|
||||||
this.country = country;
|
AutoSizeAxes = Axes.X;
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
TextAnchor = Anchor.CentreLeft;
|
||||||
|
|
||||||
AutoSizeAxes = Axes.Both;
|
if (!string.IsNullOrEmpty(country.FullName))
|
||||||
Add(text = new OsuSpriteText
|
AddLink(country.FullName, () => rankings?.ShowCountry(country));
|
||||||
{
|
|
||||||
Font = OsuFont.GetFont(size: 12),
|
|
||||||
Text = country.FullName ?? string.Empty,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OverlayColourProvider colourProvider)
|
|
||||||
{
|
|
||||||
IdleColour = colourProvider.Light2;
|
|
||||||
HoverColour = colourProvider.Content2;
|
|
||||||
|
|
||||||
Action = () => rankings?.ShowCountry(country);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -20,7 +20,8 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
{
|
{
|
||||||
protected const int TEXT_SIZE = 12;
|
protected const int TEXT_SIZE = 12;
|
||||||
private const float horizontal_inset = 20;
|
private const float horizontal_inset = 20;
|
||||||
private const float row_height = 25;
|
private const float row_height = 32;
|
||||||
|
private const float row_spacing = 3;
|
||||||
private const int items_per_page = 50;
|
private const int items_per_page = 50;
|
||||||
|
|
||||||
private readonly int page;
|
private readonly int page;
|
||||||
@ -35,7 +36,7 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
Padding = new MarginPadding { Horizontal = horizontal_inset };
|
Padding = new MarginPadding { Horizontal = horizontal_inset };
|
||||||
RowSize = new Dimension(GridSizeMode.Absolute, row_height);
|
RowSize = new Dimension(GridSizeMode.Absolute, row_height + row_spacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -47,10 +48,11 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Depth = 1f,
|
Depth = 1f,
|
||||||
Margin = new MarginPadding { Top = row_height }
|
Margin = new MarginPadding { Top = row_height + row_spacing },
|
||||||
|
Spacing = new Vector2(0, row_spacing),
|
||||||
});
|
});
|
||||||
|
|
||||||
rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground()));
|
rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground { Height = row_height }));
|
||||||
|
|
||||||
Columns = mainHeaders.Concat(CreateAdditionalHeaders()).ToArray();
|
Columns = mainHeaders.Concat(CreateAdditionalHeaders()).ToArray();
|
||||||
Content = rankings.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular();
|
Content = rankings.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular();
|
||||||
@ -68,13 +70,19 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
|
|
||||||
protected abstract Drawable[] CreateAdditionalContent(TModel item);
|
protected abstract Drawable[] CreateAdditionalContent(TModel item);
|
||||||
|
|
||||||
protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty, HighlightedColumn());
|
protected virtual string HighlightedColumn => @"Performance";
|
||||||
|
|
||||||
|
protected override Drawable CreateHeader(int index, TableColumn column)
|
||||||
|
{
|
||||||
|
var title = column?.Header ?? string.Empty;
|
||||||
|
return new HeaderText(title, title == HighlightedColumn);
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract Country GetCountry(TModel item);
|
protected abstract Country GetCountry(TModel item);
|
||||||
|
|
||||||
protected abstract Drawable CreateFlagContent(TModel item);
|
protected abstract Drawable CreateFlagContent(TModel item);
|
||||||
|
|
||||||
private OsuSpriteText createIndexDrawable(int index) => new OsuSpriteText
|
private OsuSpriteText createIndexDrawable(int index) => new RowText
|
||||||
{
|
{
|
||||||
Text = $"#{index + 1}",
|
Text = $"#{index + 1}",
|
||||||
Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.SemiBold)
|
Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.SemiBold)
|
||||||
@ -84,37 +92,36 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(7, 0),
|
Spacing = new Vector2(10, 0),
|
||||||
|
Margin = new MarginPadding { Bottom = row_spacing },
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new UpdateableFlag(GetCountry(item))
|
new UpdateableFlag(GetCountry(item))
|
||||||
{
|
{
|
||||||
Size = new Vector2(20, 13),
|
Size = new Vector2(30, 20),
|
||||||
ShowPlaceholderOnNull = false,
|
ShowPlaceholderOnNull = false,
|
||||||
},
|
},
|
||||||
CreateFlagContent(item)
|
CreateFlagContent(item)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
protected virtual string HighlightedColumn() => @"Performance";
|
protected class HeaderText : OsuSpriteText
|
||||||
|
|
||||||
private class HeaderText : OsuSpriteText
|
|
||||||
{
|
{
|
||||||
private readonly string highlighted;
|
private readonly bool isHighlighted;
|
||||||
|
|
||||||
public HeaderText(string text, string highlighted)
|
public HeaderText(string text, bool isHighlighted)
|
||||||
{
|
{
|
||||||
this.highlighted = highlighted;
|
this.isHighlighted = isHighlighted;
|
||||||
|
|
||||||
Text = text;
|
Text = text;
|
||||||
Font = OsuFont.GetFont(size: 12);
|
Font = OsuFont.GetFont(size: 12);
|
||||||
Margin = new MarginPadding { Horizontal = 10 };
|
Margin = new MarginPadding { Vertical = 5, Horizontal = 10 };
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OverlayColourProvider colourProvider)
|
private void load(OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
if (Text != highlighted)
|
if (!isHighlighted)
|
||||||
Colour = colourProvider.Foreground1;
|
Colour = colourProvider.Foreground1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +131,7 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
public RowText()
|
public RowText()
|
||||||
{
|
{
|
||||||
Font = OsuFont.GetFont(size: TEXT_SIZE);
|
Font = OsuFont.GetFont(size: TEXT_SIZE);
|
||||||
Margin = new MarginPadding { Horizontal = 10 };
|
Margin = new MarginPadding { Horizontal = 10, Bottom = row_spacing };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,6 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override string HighlightedColumn() => @"Ranked Score";
|
protected override string HighlightedColumn => @"Ranked Score";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -22,10 +22,10 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
public TableRowBackground()
|
public TableRowBackground()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = 25;
|
|
||||||
|
|
||||||
CornerRadius = 3;
|
CornerRadius = 4;
|
||||||
Masking = true;
|
Masking = true;
|
||||||
|
MaskingSmoothness = 0.5f;
|
||||||
|
|
||||||
InternalChild = background = new Box
|
InternalChild = background = new Box
|
||||||
{
|
{
|
||||||
|
@ -19,22 +19,32 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual IEnumerable<string> GradeColumns => new List<string> { "SS", "S", "A" };
|
||||||
|
|
||||||
protected override TableColumn[] CreateAdditionalHeaders() => new[]
|
protected override TableColumn[] CreateAdditionalHeaders() => new[]
|
||||||
{
|
{
|
||||||
new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
}.Concat(CreateUniqueHeaders()).Concat(new[]
|
}.Concat(CreateUniqueHeaders())
|
||||||
|
.Concat(GradeColumns.Select(grade => new TableColumn(grade, Anchor.Centre, new Dimension(GridSizeMode.AutoSize))))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
protected override Drawable CreateHeader(int index, TableColumn column)
|
||||||
{
|
{
|
||||||
new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
var title = column?.Header ?? string.Empty;
|
||||||
new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title));
|
||||||
new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
}
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
protected sealed override Country GetCountry(UserStatistics item) => item.User.Country;
|
protected sealed override Country GetCountry(UserStatistics item) => item.User.Country;
|
||||||
|
|
||||||
protected sealed override Drawable CreateFlagContent(UserStatistics item)
|
protected sealed override Drawable CreateFlagContent(UserStatistics item)
|
||||||
{
|
{
|
||||||
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE, italics: true)) { AutoSizeAxes = Axes.Both };
|
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE, italics: true))
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
TextAnchor = Anchor.CentreLeft
|
||||||
|
};
|
||||||
username.AddUserLink(item.User);
|
username.AddUserLink(item.User);
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
@ -53,5 +63,19 @@ namespace osu.Game.Overlays.Rankings.Tables
|
|||||||
protected abstract TableColumn[] CreateUniqueHeaders();
|
protected abstract TableColumn[] CreateUniqueHeaders();
|
||||||
|
|
||||||
protected abstract Drawable[] CreateUniqueContent(UserStatistics item);
|
protected abstract Drawable[] CreateUniqueContent(UserStatistics item);
|
||||||
|
|
||||||
|
private class UserTableHeaderText : HeaderText
|
||||||
|
{
|
||||||
|
public UserTableHeaderText(string text, bool isHighlighted, bool isGrade)
|
||||||
|
: base(text, isHighlighted)
|
||||||
|
{
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
// Grade columns have extra horizontal padding for readibility
|
||||||
|
Horizontal = isGrade ? 20 : 10,
|
||||||
|
Vertical = 5
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
{
|
{
|
||||||
private TriangleButton selectionButton;
|
private TriangleButton selectionButton;
|
||||||
|
|
||||||
private DirectorySelector directorySelector;
|
private OsuDirectorySelector directorySelector;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Text to display in the header to inform the user of what they are selecting.
|
/// Text to display in the header to inform the user of what they are selecting.
|
||||||
@ -91,7 +91,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
},
|
},
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
directorySelector = new DirectorySelector
|
directorySelector = new OsuDirectorySelector
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Overlays.Volume
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.ToggleMute:
|
case GlobalAction.ToggleMute:
|
||||||
|
case GlobalAction.NextVolumeMeter:
|
||||||
|
case GlobalAction.PreviousVolumeMeter:
|
||||||
ActionRequested?.Invoke(action);
|
ActionRequested?.Invoke(action);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
@ -19,13 +20,14 @@ using osu.Framework.Threading;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Volume
|
namespace osu.Game.Overlays.Volume
|
||||||
{
|
{
|
||||||
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
|
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>, IStateful<SelectionState>
|
||||||
{
|
{
|
||||||
private CircularProgress volumeCircle;
|
private CircularProgress volumeCircle;
|
||||||
private CircularProgress volumeCircleGlow;
|
private CircularProgress volumeCircleGlow;
|
||||||
@ -38,9 +40,32 @@ namespace osu.Game.Overlays.Volume
|
|||||||
private OsuSpriteText text;
|
private OsuSpriteText text;
|
||||||
private BufferedContainer maxGlow;
|
private BufferedContainer maxGlow;
|
||||||
|
|
||||||
|
private Container selectedGlowContainer;
|
||||||
|
|
||||||
private Sample sample;
|
private Sample sample;
|
||||||
private double sampleLastPlaybackTime;
|
private double sampleLastPlaybackTime;
|
||||||
|
|
||||||
|
public event Action<SelectionState> StateChanged;
|
||||||
|
|
||||||
|
private SelectionState state;
|
||||||
|
|
||||||
|
public SelectionState State
|
||||||
|
{
|
||||||
|
get => state;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (state == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state = value;
|
||||||
|
StateChanged?.Invoke(value);
|
||||||
|
|
||||||
|
updateSelectedState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const float transition_length = 500;
|
||||||
|
|
||||||
public VolumeMeter(string name, float circleSize, Color4 meterColour)
|
public VolumeMeter(string name, float circleSize, Color4 meterColour)
|
||||||
{
|
{
|
||||||
this.circleSize = circleSize;
|
this.circleSize = circleSize;
|
||||||
@ -75,7 +100,6 @@ namespace osu.Game.Overlays.Volume
|
|||||||
{
|
{
|
||||||
new BufferedContainer
|
new BufferedContainer
|
||||||
{
|
{
|
||||||
Alpha = 0.9f,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -147,6 +171,24 @@ namespace osu.Game.Overlays.Volume
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
selectedGlowContainer = new CircularContainer
|
||||||
|
{
|
||||||
|
Masking = true,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
AlwaysPresent = true,
|
||||||
|
},
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Colour = meterColour.Opacity(0.1f),
|
||||||
|
Radius = 10,
|
||||||
|
}
|
||||||
|
},
|
||||||
maxGlow = (text = new OsuSpriteText
|
maxGlow = (text = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -171,7 +213,6 @@ namespace osu.Game.Overlays.Volume
|
|||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
Alpha = 0.9f,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = backgroundColour,
|
Colour = backgroundColour,
|
||||||
},
|
},
|
||||||
@ -305,17 +346,14 @@ namespace osu.Game.Overlays.Volume
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const float transition_length = 500;
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
|
||||||
{
|
{
|
||||||
this.ScaleTo(1.04f, transition_length, Easing.OutExpo);
|
State = SelectionState.Selected;
|
||||||
return false;
|
return base.OnMouseMove(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnHoverLost(HoverLostEvent e)
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
{
|
{
|
||||||
this.ScaleTo(1f, transition_length, Easing.OutExpo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnPressed(GlobalAction action)
|
public bool OnPressed(GlobalAction action)
|
||||||
@ -326,10 +364,12 @@ namespace osu.Game.Overlays.Volume
|
|||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case GlobalAction.SelectPrevious:
|
case GlobalAction.SelectPrevious:
|
||||||
|
State = SelectionState.Selected;
|
||||||
adjust(1, false);
|
adjust(1, false);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.SelectNext:
|
case GlobalAction.SelectNext:
|
||||||
|
State = SelectionState.Selected;
|
||||||
adjust(-1, false);
|
adjust(-1, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -340,5 +380,21 @@ namespace osu.Game.Overlays.Volume
|
|||||||
public void OnReleased(GlobalAction action)
|
public void OnReleased(GlobalAction action)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateSelectedState()
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case SelectionState.Selected:
|
||||||
|
this.ScaleTo(1.04f, transition_length, Easing.OutExpo);
|
||||||
|
selectedGlowContainer.FadeIn(transition_length, Easing.OutExpo);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SelectionState.NotSelected:
|
||||||
|
this.ScaleTo(1f, transition_length, Easing.OutExpo);
|
||||||
|
selectedGlowContainer.FadeOut(transition_length, Easing.OutExpo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Overlays.Volume;
|
using osu.Game.Overlays.Volume;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -32,6 +33,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
public Bindable<bool> IsMuted { get; } = new Bindable<bool>();
|
public Bindable<bool> IsMuted { get; } = new Bindable<bool>();
|
||||||
|
|
||||||
|
private SelectionCycleFillFlowContainer<VolumeMeter> volumeMeters;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio, OsuColour colours)
|
private void load(AudioManager audio, OsuColour colours)
|
||||||
{
|
{
|
||||||
@ -53,7 +56,7 @@ namespace osu.Game.Overlays
|
|||||||
Margin = new MarginPadding(10),
|
Margin = new MarginPadding(10),
|
||||||
Current = { BindTarget = IsMuted }
|
Current = { BindTarget = IsMuted }
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
volumeMeters = new SelectionCycleFillFlowContainer<VolumeMeter>
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
@ -61,7 +64,7 @@ namespace osu.Game.Overlays
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Spacing = new Vector2(0, offset),
|
Spacing = new Vector2(0, offset),
|
||||||
Margin = new MarginPadding { Left = offset },
|
Margin = new MarginPadding { Left = offset },
|
||||||
Children = new Drawable[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker),
|
volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker),
|
||||||
volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker),
|
volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker),
|
||||||
@ -87,9 +90,9 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
volumeMeterMaster.Bindable.ValueChanged += _ => Show();
|
foreach (var volumeMeter in volumeMeters)
|
||||||
volumeMeterEffect.Bindable.ValueChanged += _ => Show();
|
volumeMeter.Bindable.ValueChanged += _ => Show();
|
||||||
volumeMeterMusic.Bindable.ValueChanged += _ => Show();
|
|
||||||
muteButton.Current.ValueChanged += _ => Show();
|
muteButton.Current.ValueChanged += _ => Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,23 +105,27 @@ namespace osu.Game.Overlays
|
|||||||
case GlobalAction.DecreaseVolume:
|
case GlobalAction.DecreaseVolume:
|
||||||
if (State.Value == Visibility.Hidden)
|
if (State.Value == Visibility.Hidden)
|
||||||
Show();
|
Show();
|
||||||
else if (volumeMeterMusic.IsHovered)
|
|
||||||
volumeMeterMusic.Decrease(amount, isPrecise);
|
|
||||||
else if (volumeMeterEffect.IsHovered)
|
|
||||||
volumeMeterEffect.Decrease(amount, isPrecise);
|
|
||||||
else
|
else
|
||||||
volumeMeterMaster.Decrease(amount, isPrecise);
|
volumeMeters.Selected?.Decrease(amount, isPrecise);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.IncreaseVolume:
|
case GlobalAction.IncreaseVolume:
|
||||||
if (State.Value == Visibility.Hidden)
|
if (State.Value == Visibility.Hidden)
|
||||||
Show();
|
Show();
|
||||||
else if (volumeMeterMusic.IsHovered)
|
|
||||||
volumeMeterMusic.Increase(amount, isPrecise);
|
|
||||||
else if (volumeMeterEffect.IsHovered)
|
|
||||||
volumeMeterEffect.Increase(amount, isPrecise);
|
|
||||||
else
|
else
|
||||||
volumeMeterMaster.Increase(amount, isPrecise);
|
volumeMeters.Selected?.Increase(amount, isPrecise);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case GlobalAction.NextVolumeMeter:
|
||||||
|
if (State.Value == Visibility.Visible)
|
||||||
|
volumeMeters.SelectNext();
|
||||||
|
Show();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case GlobalAction.PreviousVolumeMeter:
|
||||||
|
if (State.Value == Visibility.Visible)
|
||||||
|
volumeMeters.SelectPrevious();
|
||||||
|
Show();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.ToggleMute:
|
case GlobalAction.ToggleMute:
|
||||||
@ -134,6 +141,10 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
public override void Show()
|
public override void Show()
|
||||||
{
|
{
|
||||||
|
// Focus on the master meter as a default if previously hidden
|
||||||
|
if (State.Value == Visibility.Hidden)
|
||||||
|
volumeMeters.Select(volumeMeterMaster);
|
||||||
|
|
||||||
if (State.Value == Visibility.Visible)
|
if (State.Value == Visibility.Visible)
|
||||||
schedulePopOut();
|
schedulePopOut();
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.OpenGL.Textures;
|
using osu.Framework.Graphics.OpenGL.Textures;
|
||||||
|
using osu.Framework.Graphics.Shaders;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
@ -34,6 +35,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public ISampleStore SampleStore { get; }
|
public ISampleStore SampleStore { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The shader manager to be used for the ruleset.
|
||||||
|
/// </summary>
|
||||||
|
public ShaderManager ShaderManager { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ruleset config manager.
|
/// The ruleset config manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -52,6 +58,9 @@ namespace osu.Game.Rulesets.UI
|
|||||||
SampleStore = parent.Get<AudioManager>().GetSampleStore(new NamespacedResourceStore<byte[]>(resources, @"Samples"));
|
SampleStore = parent.Get<AudioManager>().GetSampleStore(new NamespacedResourceStore<byte[]>(resources, @"Samples"));
|
||||||
SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
|
SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
|
||||||
CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get<ISampleStore>()));
|
CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get<ISampleStore>()));
|
||||||
|
|
||||||
|
ShaderManager = new ShaderManager(new NamespacedResourceStore<byte[]>(resources, @"Shaders"));
|
||||||
|
CacheAs(ShaderManager = new FallbackShaderManager(ShaderManager, parent.Get<ShaderManager>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
RulesetConfigManager = parent.Get<RulesetConfigCache>().GetConfigFor(ruleset);
|
RulesetConfigManager = parent.Get<RulesetConfigCache>().GetConfigFor(ruleset);
|
||||||
@ -84,6 +93,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
SampleStore?.Dispose();
|
SampleStore?.Dispose();
|
||||||
TextureStore?.Dispose();
|
TextureStore?.Dispose();
|
||||||
|
ShaderManager?.Dispose();
|
||||||
RulesetConfigManager = null;
|
RulesetConfigManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,5 +182,26 @@ namespace osu.Game.Rulesets.UI
|
|||||||
primary?.Dispose();
|
primary?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class FallbackShaderManager : ShaderManager
|
||||||
|
{
|
||||||
|
private readonly ShaderManager primary;
|
||||||
|
private readonly ShaderManager fallback;
|
||||||
|
|
||||||
|
public FallbackShaderManager(ShaderManager primary, ShaderManager fallback)
|
||||||
|
: base(new ResourceStore<byte[]>())
|
||||||
|
{
|
||||||
|
this.primary = primary;
|
||||||
|
this.fallback = fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte[] LoadRaw(string name) => primary.LoadRaw(name) ?? fallback.LoadRaw(name);
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
primary?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,9 +56,9 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
|
|
||||||
public void DisplayFileChooser()
|
public void DisplayFileChooser()
|
||||||
{
|
{
|
||||||
FileSelector fileSelector;
|
OsuFileSelector fileSelector;
|
||||||
|
|
||||||
Target.Child = fileSelector = new FileSelector(currentFile.Value?.DirectoryName, handledExtensions)
|
Target.Child = fileSelector = new OsuFileSelector(currentFile.Value?.DirectoryName, handledExtensions)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = 400,
|
Height = 400,
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Screens.Import
|
|||||||
{
|
{
|
||||||
public override bool HideOverlaysOnEnter => true;
|
public override bool HideOverlaysOnEnter => true;
|
||||||
|
|
||||||
private FileSelector fileSelector;
|
private OsuFileSelector fileSelector;
|
||||||
private Container contentContainer;
|
private Container contentContainer;
|
||||||
private TextFlowContainer currentFileText;
|
private TextFlowContainer currentFileText;
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ namespace osu.Game.Screens.Import
|
|||||||
Colour = colours.GreySeafoamDark,
|
Colour = colours.GreySeafoamDark,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
fileSelector = new FileSelector(validFileExtensions: game.HandledExtensions.ToArray())
|
fileSelector = new OsuFileSelector(validFileExtensions: game.HandledExtensions.ToArray())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Width = 0.65f
|
Width = 0.65f
|
||||||
|
@ -2,23 +2,24 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Humanizer;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using Humanizer;
|
using osuTK;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
@ -46,13 +47,13 @@ namespace osu.Game.Screens.Play
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Action that is invoked when <see cref="GlobalAction.Select"/> is triggered.
|
/// Action that is invoked when <see cref="GlobalAction.Select"/> is triggered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual Action SelectAction => () => InternalButtons.Children.FirstOrDefault(f => f.Selected.Value)?.Click();
|
protected virtual Action SelectAction => () => InternalButtons.Selected?.Click();
|
||||||
|
|
||||||
public abstract string Header { get; }
|
public abstract string Header { get; }
|
||||||
|
|
||||||
public abstract string Description { get; }
|
public abstract string Description { get; }
|
||||||
|
|
||||||
protected ButtonContainer InternalButtons;
|
protected SelectionCycleFillFlowContainer<DialogButton> InternalButtons;
|
||||||
public IReadOnlyList<DialogButton> Buttons => InternalButtons;
|
public IReadOnlyList<DialogButton> Buttons => InternalButtons;
|
||||||
|
|
||||||
private FillFlowContainer retryCounterContainer;
|
private FillFlowContainer retryCounterContainer;
|
||||||
@ -116,7 +117,7 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
InternalButtons = new ButtonContainer
|
InternalButtons = new SelectionCycleFillFlowContainer<DialogButton>
|
||||||
{
|
{
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
@ -183,8 +184,6 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
button.Selected.ValueChanged += selected => buttonSelectionChanged(button, selected.NewValue);
|
|
||||||
|
|
||||||
InternalButtons.Add(button);
|
InternalButtons.Add(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,14 +215,6 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buttonSelectionChanged(DialogButton button, bool isSelected)
|
|
||||||
{
|
|
||||||
if (!isSelected)
|
|
||||||
InternalButtons.Deselect();
|
|
||||||
else
|
|
||||||
InternalButtons.Select(button);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateRetryCount()
|
private void updateRetryCount()
|
||||||
{
|
{
|
||||||
// "You've retried 1,065 times in this session"
|
// "You've retried 1,065 times in this session"
|
||||||
@ -255,46 +246,6 @@ namespace osu.Game.Screens.Play
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class ButtonContainer : FillFlowContainer<DialogButton>
|
|
||||||
{
|
|
||||||
private int selectedIndex = -1;
|
|
||||||
|
|
||||||
private void setSelected(int value)
|
|
||||||
{
|
|
||||||
if (selectedIndex == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Deselect the previously-selected button
|
|
||||||
if (selectedIndex != -1)
|
|
||||||
this[selectedIndex].Selected.Value = false;
|
|
||||||
|
|
||||||
selectedIndex = value;
|
|
||||||
|
|
||||||
// Select the newly-selected button
|
|
||||||
if (selectedIndex != -1)
|
|
||||||
this[selectedIndex].Selected.Value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SelectNext()
|
|
||||||
{
|
|
||||||
if (selectedIndex == -1 || selectedIndex == Count - 1)
|
|
||||||
setSelected(0);
|
|
||||||
else
|
|
||||||
setSelected(selectedIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SelectPrevious()
|
|
||||||
{
|
|
||||||
if (selectedIndex == -1 || selectedIndex == 0)
|
|
||||||
setSelected(Count - 1);
|
|
||||||
else
|
|
||||||
setSelected(selectedIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Deselect() => setSelected(-1);
|
|
||||||
public void Select(DialogButton button) => setSelected(IndexOf(button));
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Button : DialogButton
|
private class Button : DialogButton
|
||||||
{
|
{
|
||||||
// required to ensure keyboard navigation always starts from an extremity (unless the cursor is moved)
|
// required to ensure keyboard navigation always starts from an extremity (unless the cursor is moved)
|
||||||
@ -302,7 +253,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
{
|
{
|
||||||
Selected.Value = true;
|
State = SelectionState.Selected;
|
||||||
return base.OnMouseMove(e);
|
return base.OnMouseMove(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,9 +83,9 @@ namespace osu.Game.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
beatmapSkins.BindValueChanged(_ => OnSourceChanged());
|
beatmapSkins.BindValueChanged(_ => TriggerSourceChanged());
|
||||||
beatmapColours.BindValueChanged(_ => OnSourceChanged());
|
beatmapColours.BindValueChanged(_ => TriggerSourceChanged());
|
||||||
beatmapHitsounds.BindValueChanged(_ => OnSourceChanged());
|
beatmapHitsounds.BindValueChanged(_ => TriggerSourceChanged());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -46,57 +48,51 @@ namespace osu.Game.Skinning
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private ISkinSource parentSource;
|
|
||||||
|
|
||||||
private ResourceStoreBackedSkin rulesetResourcesSkin;
|
private ResourceStoreBackedSkin rulesetResourcesSkin;
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
{
|
{
|
||||||
parentSource = parent.Get<ISkinSource>();
|
|
||||||
parentSource.SourceChanged += OnSourceChanged;
|
|
||||||
|
|
||||||
if (Ruleset.CreateResourceStore() is IResourceStore<byte[]> resources)
|
if (Ruleset.CreateResourceStore() is IResourceStore<byte[]> resources)
|
||||||
rulesetResourcesSkin = new ResourceStoreBackedSkin(resources, parent.Get<GameHost>(), parent.Get<AudioManager>());
|
rulesetResourcesSkin = new ResourceStoreBackedSkin(resources, parent.Get<GameHost>(), parent.Get<AudioManager>());
|
||||||
|
|
||||||
// ensure sources are populated and ready for use before childrens' asynchronous load flow.
|
|
||||||
UpdateSkinSources();
|
|
||||||
|
|
||||||
return base.CreateChildDependencies(parent);
|
return base.CreateChildDependencies(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnSourceChanged()
|
protected override void OnSourceChanged()
|
||||||
{
|
{
|
||||||
UpdateSkinSources();
|
ResetSources();
|
||||||
base.OnSourceChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void UpdateSkinSources()
|
// Populate a local list first so we can adjust the returned order as we go.
|
||||||
{
|
var sources = new List<ISkin>();
|
||||||
SkinSources.Clear();
|
|
||||||
|
|
||||||
foreach (var skin in parentSource.AllSources)
|
Debug.Assert(ParentSource != null);
|
||||||
|
|
||||||
|
foreach (var skin in ParentSource.AllSources)
|
||||||
{
|
{
|
||||||
switch (skin)
|
switch (skin)
|
||||||
{
|
{
|
||||||
case LegacySkin legacySkin:
|
case LegacySkin legacySkin:
|
||||||
SkinSources.Add(GetLegacyRulesetTransformedSkin(legacySkin));
|
sources.Add(GetLegacyRulesetTransformedSkin(legacySkin));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
SkinSources.Add(skin);
|
sources.Add(skin);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int lastDefaultSkinIndex = SkinSources.IndexOf(SkinSources.OfType<DefaultSkin>().LastOrDefault());
|
int lastDefaultSkinIndex = sources.IndexOf(sources.OfType<DefaultSkin>().LastOrDefault());
|
||||||
|
|
||||||
// Ruleset resources should be given the ability to override game-wide defaults
|
// Ruleset resources should be given the ability to override game-wide defaults
|
||||||
// This is achieved by placing them before the last instance of DefaultSkin.
|
// This is achieved by placing them before the last instance of DefaultSkin.
|
||||||
// Note that DefaultSkin may not be present in some test scenes.
|
// Note that DefaultSkin may not be present in some test scenes.
|
||||||
if (lastDefaultSkinIndex >= 0)
|
if (lastDefaultSkinIndex >= 0)
|
||||||
SkinSources.Insert(lastDefaultSkinIndex, rulesetResourcesSkin);
|
sources.Insert(lastDefaultSkinIndex, rulesetResourcesSkin);
|
||||||
else
|
else
|
||||||
SkinSources.Add(rulesetResourcesSkin);
|
sources.Add(rulesetResourcesSkin);
|
||||||
|
|
||||||
|
foreach (var skin in sources)
|
||||||
|
AddSource(skin);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ISkin GetLegacyRulesetTransformedSkin(ISkin legacySkin)
|
protected ISkin GetLegacyRulesetTransformedSkin(ISkin legacySkin)
|
||||||
@ -115,9 +111,6 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
if (parentSource != null)
|
|
||||||
parentSource.SourceChanged -= OnSourceChanged;
|
|
||||||
|
|
||||||
rulesetResourcesSkin?.Dispose();
|
rulesetResourcesSkin?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Linq;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
@ -24,19 +22,8 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
public event Action SourceChanged;
|
public event Action SourceChanged;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Skins which should be exposed by this container, in order of lookup precedence.
|
|
||||||
/// </summary>
|
|
||||||
protected readonly BindableList<ISkin> SkinSources = new BindableList<ISkin>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A dictionary mapping each <see cref="ISkin"/> from the <see cref="SkinSources"/>
|
|
||||||
/// to one that performs the "allow lookup" checks before proceeding with a lookup.
|
|
||||||
/// </summary>
|
|
||||||
private readonly Dictionary<ISkin, DisableableSkinSource> disableableSkinSources = new Dictionary<ISkin, DisableableSkinSource>();
|
|
||||||
|
|
||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
private ISkinSource fallbackSource;
|
protected ISkinSource ParentSource { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether falling back to parent <see cref="ISkinSource"/>s is allowed in this container.
|
/// Whether falling back to parent <see cref="ISkinSource"/>s is allowed in this container.
|
||||||
@ -53,6 +40,11 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
protected virtual bool AllowColourLookup => true;
|
protected virtual bool AllowColourLookup => true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary mapping each <see cref="ISkin"/> source to a wrapper which handles lookup allowances.
|
||||||
|
/// </summary>
|
||||||
|
private readonly List<(ISkin skin, DisableableSkinSource wrapped)> skinSources = new List<(ISkin, DisableableSkinSource)>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new <see cref="SkinProvidingContainer"/> initialised with a single skin source.
|
/// Constructs a new <see cref="SkinProvidingContainer"/> initialised with a single skin source.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -60,87 +52,56 @@ namespace osu.Game.Skinning
|
|||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
if (skin != null)
|
if (skin != null)
|
||||||
SkinSources.Add(skin);
|
AddSource(skin);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new <see cref="SkinProvidingContainer"/> with no sources.
|
/// Constructs a new <see cref="SkinProvidingContainer"/> with no sources.
|
||||||
/// Implementations can add or change sources through the <see cref="SkinSources"/> list.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected SkinProvidingContainer()
|
protected SkinProvidingContainer()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
SkinSources.BindCollectionChanged(((_, args) =>
|
|
||||||
{
|
|
||||||
switch (args.Action)
|
|
||||||
{
|
|
||||||
case NotifyCollectionChangedAction.Add:
|
|
||||||
foreach (var skin in args.NewItems.Cast<ISkin>())
|
|
||||||
{
|
|
||||||
disableableSkinSources.Add(skin, new DisableableSkinSource(skin, this));
|
|
||||||
|
|
||||||
if (skin is ISkinSource source)
|
|
||||||
source.SourceChanged += OnSourceChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
|
|
||||||
case NotifyCollectionChangedAction.Reset:
|
|
||||||
case NotifyCollectionChangedAction.Remove:
|
|
||||||
foreach (var skin in args.OldItems.Cast<ISkin>())
|
|
||||||
{
|
{
|
||||||
disableableSkinSources.Remove(skin);
|
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
|
||||||
if (skin is ISkinSource source)
|
ParentSource = dependencies.Get<ISkinSource>();
|
||||||
source.SourceChanged -= OnSourceChanged;
|
if (ParentSource != null)
|
||||||
}
|
ParentSource.SourceChanged += TriggerSourceChanged;
|
||||||
|
|
||||||
break;
|
dependencies.CacheAs<ISkinSource>(this);
|
||||||
|
|
||||||
case NotifyCollectionChangedAction.Replace:
|
TriggerSourceChanged();
|
||||||
foreach (var skin in args.OldItems.Cast<ISkin>())
|
|
||||||
{
|
|
||||||
disableableSkinSources.Remove(skin);
|
|
||||||
|
|
||||||
if (skin is ISkinSource source)
|
return dependencies;
|
||||||
source.SourceChanged -= OnSourceChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var skin in args.NewItems.Cast<ISkin>())
|
|
||||||
{
|
|
||||||
disableableSkinSources.Add(skin, new DisableableSkinSource(skin, this));
|
|
||||||
|
|
||||||
if (skin is ISkinSource source)
|
|
||||||
source.SourceChanged += OnSourceChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction)
|
public ISkin FindProvider(Func<ISkin, bool> lookupFunction)
|
||||||
{
|
{
|
||||||
foreach (var skin in SkinSources)
|
foreach (var (skin, lookupWrapper) in skinSources)
|
||||||
{
|
{
|
||||||
if (lookupFunction(disableableSkinSources[skin]))
|
if (lookupFunction(lookupWrapper))
|
||||||
return skin;
|
return skin;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fallbackSource?.FindProvider(lookupFunction);
|
if (!AllowFallingBackToParent)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return ParentSource?.FindProvider(lookupFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ISkin> AllSources
|
public IEnumerable<ISkin> AllSources
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
foreach (var skin in SkinSources)
|
foreach (var i in skinSources)
|
||||||
yield return skin;
|
yield return i.skin;
|
||||||
|
|
||||||
if (fallbackSource != null)
|
if (AllowFallingBackToParent && ParentSource != null)
|
||||||
{
|
{
|
||||||
foreach (var skin in fallbackSource.AllSources)
|
foreach (var skin in ParentSource.AllSources)
|
||||||
yield return skin;
|
yield return skin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,68 +109,110 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
public Drawable GetDrawableComponent(ISkinComponent component)
|
public Drawable GetDrawableComponent(ISkinComponent component)
|
||||||
{
|
{
|
||||||
foreach (var skin in SkinSources)
|
foreach (var (_, lookupWrapper) in skinSources)
|
||||||
{
|
{
|
||||||
Drawable sourceDrawable;
|
Drawable sourceDrawable;
|
||||||
if ((sourceDrawable = disableableSkinSources[skin]?.GetDrawableComponent(component)) != null)
|
if ((sourceDrawable = lookupWrapper.GetDrawableComponent(component)) != null)
|
||||||
return sourceDrawable;
|
return sourceDrawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fallbackSource?.GetDrawableComponent(component);
|
if (!AllowFallingBackToParent)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return ParentSource?.GetDrawableComponent(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT)
|
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT)
|
||||||
{
|
{
|
||||||
foreach (var skin in SkinSources)
|
foreach (var (_, lookupWrapper) in skinSources)
|
||||||
{
|
{
|
||||||
Texture sourceTexture;
|
Texture sourceTexture;
|
||||||
if ((sourceTexture = disableableSkinSources[skin]?.GetTexture(componentName, wrapModeS, wrapModeT)) != null)
|
if ((sourceTexture = lookupWrapper.GetTexture(componentName, wrapModeS, wrapModeT)) != null)
|
||||||
return sourceTexture;
|
return sourceTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fallbackSource?.GetTexture(componentName, wrapModeS, wrapModeT);
|
if (!AllowFallingBackToParent)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return ParentSource?.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ISample GetSample(ISampleInfo sampleInfo)
|
public ISample GetSample(ISampleInfo sampleInfo)
|
||||||
{
|
{
|
||||||
foreach (var skin in SkinSources)
|
foreach (var (_, lookupWrapper) in skinSources)
|
||||||
{
|
{
|
||||||
ISample sourceSample;
|
ISample sourceSample;
|
||||||
if ((sourceSample = disableableSkinSources[skin]?.GetSample(sampleInfo)) != null)
|
if ((sourceSample = lookupWrapper.GetSample(sampleInfo)) != null)
|
||||||
return sourceSample;
|
return sourceSample;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fallbackSource?.GetSample(sampleInfo);
|
if (!AllowFallingBackToParent)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return ParentSource?.GetSample(sampleInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
{
|
{
|
||||||
foreach (var skin in SkinSources)
|
foreach (var (_, lookupWrapper) in skinSources)
|
||||||
{
|
{
|
||||||
IBindable<TValue> bindable;
|
IBindable<TValue> bindable;
|
||||||
if ((bindable = disableableSkinSources[skin]?.GetConfig<TLookup, TValue>(lookup)) != null)
|
if ((bindable = lookupWrapper.GetConfig<TLookup, TValue>(lookup)) != null)
|
||||||
return bindable;
|
return bindable;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fallbackSource?.GetConfig<TLookup, TValue>(lookup);
|
if (!AllowFallingBackToParent)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return ParentSource?.GetConfig<TLookup, TValue>(lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnSourceChanged() => SourceChanged?.Invoke();
|
/// <summary>
|
||||||
|
/// Add a new skin to this provider. Will be added to the end of the lookup order precedence.
|
||||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
/// </summary>
|
||||||
|
/// <param name="skin">The skin to add.</param>
|
||||||
|
protected void AddSource(ISkin skin)
|
||||||
{
|
{
|
||||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
skinSources.Add((skin, new DisableableSkinSource(skin, this)));
|
||||||
|
|
||||||
if (AllowFallingBackToParent)
|
if (skin is ISkinSource source)
|
||||||
{
|
source.SourceChanged += TriggerSourceChanged;
|
||||||
fallbackSource = dependencies.Get<ISkinSource>();
|
|
||||||
if (fallbackSource != null)
|
|
||||||
fallbackSource.SourceChanged += OnSourceChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies.CacheAs<ISkinSource>(this);
|
/// <summary>
|
||||||
|
/// Remove a skin from this provider.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="skin">The skin to remove.</param>
|
||||||
|
protected void RemoveSource(ISkin skin)
|
||||||
|
{
|
||||||
|
if (skinSources.RemoveAll(s => s.skin == skin) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
return dependencies;
|
if (skin is ISkinSource source)
|
||||||
|
source.SourceChanged -= TriggerSourceChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all skin sources.
|
||||||
|
/// </summary>
|
||||||
|
protected void ResetSources()
|
||||||
|
{
|
||||||
|
foreach (var i in skinSources.ToArray())
|
||||||
|
RemoveSource(i.skin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when any source has changed (either <see cref="ParentSource"/> or a source registered via <see cref="AddSource"/>).
|
||||||
|
/// This is also invoked once initially during <see cref="CreateChildDependencies"/> to ensure sources are ready for children consumption.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnSourceChanged() { }
|
||||||
|
|
||||||
|
protected void TriggerSourceChanged()
|
||||||
|
{
|
||||||
|
// Expose to implementations, giving them a chance to react before notifying external consumers.
|
||||||
|
OnSourceChanged();
|
||||||
|
|
||||||
|
SourceChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
@ -219,11 +222,14 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
if (fallbackSource != null)
|
if (ParentSource != null)
|
||||||
fallbackSource.SourceChanged -= OnSourceChanged;
|
ParentSource.SourceChanged -= TriggerSourceChanged;
|
||||||
|
|
||||||
foreach (var source in SkinSources.OfType<ISkinSource>())
|
foreach (var i in skinSources)
|
||||||
source.SourceChanged -= OnSourceChanged;
|
{
|
||||||
|
if (i.skin is ISkinSource source)
|
||||||
|
source.SourceChanged -= TriggerSourceChanged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DisableableSkinSource : ISkin
|
private class DisableableSkinSource : ISkin
|
||||||
|
Loading…
x
Reference in New Issue
Block a user