mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 06:03:08 +08:00
Merge remote-tracking branch 'origin/master' into import-stable-scores
This commit is contained in:
commit
55216dffb7
@ -72,7 +72,7 @@ If the build fails, try to restore nuget packages with `dotnet restore`.
|
|||||||
|
|
||||||
On Linux, the environment variable `LD_LIBRARY_PATH` must point to the build directory, located at `osu.Desktop/bin/Debug/$NETCORE_VERSION`.
|
On Linux, the environment variable `LD_LIBRARY_PATH` must point to the build directory, located at `osu.Desktop/bin/Debug/$NETCORE_VERSION`.
|
||||||
|
|
||||||
`$NETCORE_VERSION` is the version of .NET Core SDK. You can have it with `grep TargetFramework osu.Desktop/osu.Desktop.csproj | sed -r 's/.*>(.*)<\/.*/\1/'`.
|
`$NETCORE_VERSION` is the version of the targeted .NET Core SDK. You can check it by running `grep TargetFramework osu.Desktop/osu.Desktop.csproj | sed -r 's/.*>(.*)<\/.*/\1/'`.
|
||||||
|
|
||||||
For example, you can run osu! with the following command:
|
For example, you can run osu! with the following command:
|
||||||
|
|
||||||
|
105
osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
Normal file
105
osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneCatcher : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(CatcherSprite),
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly Container container;
|
||||||
|
|
||||||
|
public TestSceneCatcher()
|
||||||
|
{
|
||||||
|
Child = container = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AddStep("show default catcher implementation", () => { container.Child = new CatcherSprite(); });
|
||||||
|
|
||||||
|
AddStep("show custom catcher implementation", () =>
|
||||||
|
{
|
||||||
|
container.Child = new CatchCustomSkinSourceContainer
|
||||||
|
{
|
||||||
|
Child = new CatcherSprite()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CatcherCustomSkin : Container
|
||||||
|
{
|
||||||
|
public CatcherCustomSkin()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Blue
|
||||||
|
},
|
||||||
|
new SpriteText
|
||||||
|
{
|
||||||
|
Text = "custom"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cached(typeof(ISkinSource))]
|
||||||
|
private class CatchCustomSkinSourceContainer : Container, ISkinSource
|
||||||
|
{
|
||||||
|
public event Action SourceChanged
|
||||||
|
{
|
||||||
|
add { }
|
||||||
|
remove { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Drawable GetDrawableComponent(string componentName)
|
||||||
|
{
|
||||||
|
switch (componentName)
|
||||||
|
{
|
||||||
|
case "Play/Catch/fruit-catcher-idle":
|
||||||
|
return new CatcherCustomSkin();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SampleChannel GetSample(string sampleName) =>
|
||||||
|
throw new NotImplementedException();
|
||||||
|
|
||||||
|
public Texture GetTexture(string componentName) =>
|
||||||
|
throw new NotImplementedException();
|
||||||
|
|
||||||
|
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration =>
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,8 +6,6 @@ using System.Linq;
|
|||||||
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;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Graphics.Textures;
|
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -141,7 +139,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
caughtFruit = new Container<DrawableHitObject>
|
caughtFruit = new Container<DrawableHitObject>
|
||||||
{
|
{
|
||||||
@ -212,7 +210,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
|
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Sprite createCatcherSprite() => new CatcherSprite();
|
private Drawable createCatcherSprite() => new CatcherSprite();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a caught fruit to the catcher's stack.
|
/// Add a caught fruit to the catcher's stack.
|
||||||
@ -444,23 +442,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
fruit.Expire();
|
fruit.Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CatcherSprite : Sprite
|
|
||||||
{
|
|
||||||
public CatcherSprite()
|
|
||||||
{
|
|
||||||
Size = new Vector2(CATCHER_SIZE);
|
|
||||||
|
|
||||||
// Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
|
|
||||||
OriginPosition = new Vector2(-0.02f, 0.06f) * CATCHER_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(TextureStore textures)
|
|
||||||
{
|
|
||||||
Texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
Normal file
33
osu.Game.Rulesets.Catch/UI/CatcherSprite.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
|
{
|
||||||
|
public class CatcherSprite : CompositeDrawable
|
||||||
|
{
|
||||||
|
public CatcherSprite()
|
||||||
|
{
|
||||||
|
Size = new Vector2(CatcherArea.CATCHER_SIZE);
|
||||||
|
|
||||||
|
// Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
|
||||||
|
OriginPosition = new Vector2(-0.02f, 0.06f) * CatcherArea.CATCHER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
InternalChild = new SkinnableSprite(@"Play/Catch/fruit-catcher-idle")
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,6 @@
|
|||||||
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;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(TextureStore textures)
|
private void load(TextureStore textures)
|
||||||
{
|
{
|
||||||
Child = new SkinnableDrawable("Play/osu/approachcircle", name => new Sprite { Texture = textures.Get(name) });
|
Child = new SkinnableSprite("Play/osu/approachcircle");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -73,7 +74,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
{
|
{
|
||||||
case OsuAction.LeftButton:
|
case OsuAction.LeftButton:
|
||||||
case OsuAction.RightButton:
|
case OsuAction.RightButton:
|
||||||
if (--downCount == 0)
|
// Todo: Math.Max() is required as a temporary measure to address https://github.com/ppy/osu-framework/issues/2576
|
||||||
|
downCount = Math.Max(0, downCount - 1);
|
||||||
|
|
||||||
|
if (downCount == 0)
|
||||||
updateExpandedState();
|
updateExpandedState();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
// ReSharper disable once AccessToModifiedClosure
|
// ReSharper disable once AccessToModifiedClosure
|
||||||
manager.ItemAdded += (_, __) => Interlocked.Increment(ref itemAddRemoveFireCount);
|
manager.ItemAdded += _ => Interlocked.Increment(ref itemAddRemoveFireCount);
|
||||||
manager.ItemRemoved += _ => Interlocked.Increment(ref itemAddRemoveFireCount);
|
manager.ItemRemoved += _ => Interlocked.Increment(ref itemAddRemoveFireCount);
|
||||||
|
|
||||||
var imported = await LoadOszIntoOsu(osu);
|
var imported = await LoadOszIntoOsu(osu);
|
||||||
|
@ -14,94 +14,132 @@ namespace osu.Game.Tests.Visual.Components
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneIdleTracker : ManualInputManagerTestScene
|
public class TestSceneIdleTracker : ManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
private readonly IdleTrackingBox box1;
|
private IdleTrackingBox box1;
|
||||||
private readonly IdleTrackingBox box2;
|
private IdleTrackingBox box2;
|
||||||
private readonly IdleTrackingBox box3;
|
private IdleTrackingBox box3;
|
||||||
private readonly IdleTrackingBox box4;
|
private IdleTrackingBox box4;
|
||||||
|
|
||||||
public TestSceneIdleTracker()
|
private IdleTrackingBox[] boxes;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
InputManager.MoveMouseTo(Vector2.Zero);
|
||||||
|
|
||||||
|
Children = boxes = new[]
|
||||||
{
|
{
|
||||||
box1 = new IdleTrackingBox(1000)
|
box1 = new IdleTrackingBox(2000)
|
||||||
{
|
{
|
||||||
|
Name = "TopLeft",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.Red,
|
Colour = Color4.Red,
|
||||||
Anchor = Anchor.TopLeft,
|
Anchor = Anchor.TopLeft,
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
},
|
},
|
||||||
box2 = new IdleTrackingBox(2000)
|
box2 = new IdleTrackingBox(4000)
|
||||||
{
|
{
|
||||||
|
Name = "TopRight",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.Green,
|
Colour = Color4.Green,
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
},
|
},
|
||||||
box3 = new IdleTrackingBox(3000)
|
box3 = new IdleTrackingBox(6000)
|
||||||
{
|
{
|
||||||
|
Name = "BottomLeft",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.Blue,
|
Colour = Color4.Blue,
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
},
|
},
|
||||||
box4 = new IdleTrackingBox(4000)
|
box4 = new IdleTrackingBox(8000)
|
||||||
{
|
{
|
||||||
|
Name = "BottomRight",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.Orange,
|
Colour = Color4.Orange,
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestNudge()
|
public void TestNudge()
|
||||||
{
|
{
|
||||||
AddStep("move mouse to top left", () => InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.Centre));
|
AddStep("move to top left", () => InputManager.MoveMouseTo(box1));
|
||||||
|
|
||||||
AddUntilStep("Wait for all idle", () => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle);
|
waitForAllIdle();
|
||||||
|
|
||||||
AddStep("nudge mouse", () => InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.Centre + new Vector2(1)));
|
AddStep("nudge mouse", () => InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.Centre + new Vector2(1)));
|
||||||
|
|
||||||
AddAssert("check not idle", () => !box1.IsIdle);
|
checkIdleStatus(1, false);
|
||||||
AddAssert("check idle", () => box2.IsIdle);
|
checkIdleStatus(2, true);
|
||||||
AddAssert("check idle", () => box3.IsIdle);
|
checkIdleStatus(3, true);
|
||||||
AddAssert("check idle", () => box4.IsIdle);
|
checkIdleStatus(4, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestMovement()
|
public void TestMovement()
|
||||||
{
|
{
|
||||||
AddStep("move mouse", () => InputManager.MoveMouseTo(box2.ScreenSpaceDrawQuad.Centre));
|
AddStep("move to top right", () => InputManager.MoveMouseTo(box2));
|
||||||
|
|
||||||
AddAssert("check not idle", () => box1.IsIdle);
|
checkIdleStatus(1, true);
|
||||||
AddAssert("check not idle", () => !box2.IsIdle);
|
checkIdleStatus(2, false);
|
||||||
AddAssert("check idle", () => box3.IsIdle);
|
checkIdleStatus(3, true);
|
||||||
AddAssert("check idle", () => box4.IsIdle);
|
checkIdleStatus(4, true);
|
||||||
|
|
||||||
AddStep("move mouse", () => InputManager.MoveMouseTo(box3.ScreenSpaceDrawQuad.Centre));
|
AddStep("move to bottom left", () => InputManager.MoveMouseTo(box3));
|
||||||
AddStep("move mouse", () => InputManager.MoveMouseTo(box4.ScreenSpaceDrawQuad.Centre));
|
AddStep("move to bottom right", () => InputManager.MoveMouseTo(box4));
|
||||||
|
|
||||||
AddAssert("check not idle", () => box1.IsIdle);
|
checkIdleStatus(1, true);
|
||||||
AddAssert("check not idle", () => !box2.IsIdle);
|
checkIdleStatus(2, false);
|
||||||
AddAssert("check idle", () => !box3.IsIdle);
|
checkIdleStatus(3, false);
|
||||||
AddAssert("check idle", () => !box4.IsIdle);
|
checkIdleStatus(4, false);
|
||||||
|
|
||||||
AddUntilStep("Wait for all idle", () => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle);
|
waitForAllIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestTimings()
|
public void TestTimings()
|
||||||
{
|
{
|
||||||
AddStep("move mouse", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre));
|
AddStep("move to centre", () => InputManager.MoveMouseTo(Content));
|
||||||
|
|
||||||
|
checkIdleStatus(1, false);
|
||||||
|
checkIdleStatus(2, false);
|
||||||
|
checkIdleStatus(3, false);
|
||||||
|
checkIdleStatus(4, false);
|
||||||
|
|
||||||
AddAssert("check not idle", () => !box1.IsIdle && !box2.IsIdle && !box3.IsIdle && !box4.IsIdle);
|
|
||||||
AddUntilStep("Wait for idle", () => box1.IsIdle);
|
AddUntilStep("Wait for idle", () => box1.IsIdle);
|
||||||
AddAssert("check not idle", () => !box2.IsIdle && !box3.IsIdle && !box4.IsIdle);
|
|
||||||
|
checkIdleStatus(1, true);
|
||||||
|
checkIdleStatus(2, false);
|
||||||
|
checkIdleStatus(3, false);
|
||||||
|
checkIdleStatus(4, false);
|
||||||
|
|
||||||
AddUntilStep("Wait for idle", () => box2.IsIdle);
|
AddUntilStep("Wait for idle", () => box2.IsIdle);
|
||||||
AddAssert("check not idle", () => !box3.IsIdle && !box4.IsIdle);
|
|
||||||
|
checkIdleStatus(1, true);
|
||||||
|
checkIdleStatus(2, true);
|
||||||
|
checkIdleStatus(3, false);
|
||||||
|
checkIdleStatus(4, false);
|
||||||
|
|
||||||
AddUntilStep("Wait for idle", () => box3.IsIdle);
|
AddUntilStep("Wait for idle", () => box3.IsIdle);
|
||||||
|
|
||||||
|
checkIdleStatus(1, true);
|
||||||
|
checkIdleStatus(2, true);
|
||||||
|
checkIdleStatus(3, true);
|
||||||
|
checkIdleStatus(4, false);
|
||||||
|
|
||||||
|
waitForAllIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkIdleStatus(int box, bool expectedIdle)
|
||||||
|
{
|
||||||
|
AddAssert($"box {box} is {(expectedIdle ? "idle" : "active")}", () => boxes[box - 1].IsIdle == expectedIdle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForAllIdle()
|
||||||
|
{
|
||||||
AddUntilStep("Wait for all idle", () => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle);
|
AddUntilStep("Wait for all idle", () => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +137,22 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
exitAndConfirm();
|
exitAndConfirm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestExitViaHoldToExit()
|
||||||
|
{
|
||||||
|
AddStep("exit", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(Player.HUDOverlay.HoldToQuit.First(c => c is HoldToConfirmContainer));
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
confirmPaused();
|
||||||
|
|
||||||
|
AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
|
||||||
|
exitAndConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestExitFromPause()
|
public void TestExitFromPause()
|
||||||
{
|
{
|
||||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
{
|
{
|
||||||
typeof(ToolbarButton),
|
typeof(ToolbarButton),
|
||||||
typeof(ToolbarRulesetSelector),
|
typeof(ToolbarRulesetSelector),
|
||||||
typeof(ToolbarRulesetButton),
|
typeof(ToolbarRulesetTabButton),
|
||||||
typeof(ToolbarNotificationButton),
|
typeof(ToolbarNotificationButton),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
95
osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs
Normal file
95
osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Overlays.BeatmapSet;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneBeatmapAvailability : OsuTestScene
|
||||||
|
{
|
||||||
|
private readonly BeatmapAvailability container;
|
||||||
|
|
||||||
|
public TestSceneBeatmapAvailability()
|
||||||
|
{
|
||||||
|
Add(container = new BeatmapAvailability());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUndownloadableWithLink()
|
||||||
|
{
|
||||||
|
AddStep("set undownloadable beatmapset with link", () => container.BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
|
{
|
||||||
|
Availability = new BeatmapSetOnlineAvailability
|
||||||
|
{
|
||||||
|
DownloadDisabled = true,
|
||||||
|
ExternalLink = @"https://osu.ppy.sh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
visiblityAssert(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUndownloadableNoLink()
|
||||||
|
{
|
||||||
|
AddStep("set undownloadable beatmapset without link", () => container.BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
|
{
|
||||||
|
Availability = new BeatmapSetOnlineAvailability
|
||||||
|
{
|
||||||
|
DownloadDisabled = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
visiblityAssert(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPartsRemovedWithLink()
|
||||||
|
{
|
||||||
|
AddStep("set parts-removed beatmapset with link", () => container.BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
|
{
|
||||||
|
Availability = new BeatmapSetOnlineAvailability
|
||||||
|
{
|
||||||
|
DownloadDisabled = false,
|
||||||
|
ExternalLink = @"https://osu.ppy.sh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
visiblityAssert(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNormal()
|
||||||
|
{
|
||||||
|
AddStep("set normal beatmapset", () => container.BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
|
{
|
||||||
|
Availability = new BeatmapSetOnlineAvailability
|
||||||
|
{
|
||||||
|
DownloadDisabled = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
visiblityAssert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void visiblityAssert(bool shown)
|
||||||
|
{
|
||||||
|
AddAssert($"is container {(shown ? "visible" : "hidden")}", () => container.Alpha == (shown ? 1 : 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneBeatmapSetOverlay : OsuTestScene
|
public class TestSceneBeatmapSetOverlay : OsuTestScene
|
||||||
{
|
{
|
||||||
private readonly BeatmapSetOverlay overlay;
|
private readonly TestBeatmapSetOverlay overlay;
|
||||||
|
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
@ -39,21 +39,22 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
typeof(Info),
|
typeof(Info),
|
||||||
typeof(PreviewButton),
|
typeof(PreviewButton),
|
||||||
typeof(SuccessRate),
|
typeof(SuccessRate),
|
||||||
|
typeof(BeatmapAvailability),
|
||||||
};
|
};
|
||||||
|
|
||||||
private RulesetInfo maniaRuleset;
|
|
||||||
private RulesetInfo taikoRuleset;
|
private RulesetInfo taikoRuleset;
|
||||||
|
private RulesetInfo maniaRuleset;
|
||||||
|
|
||||||
public TestSceneBeatmapSetOverlay()
|
public TestSceneBeatmapSetOverlay()
|
||||||
{
|
{
|
||||||
Add(overlay = new BeatmapSetOverlay());
|
Add(overlay = new TestBeatmapSetOverlay());
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(RulesetStore rulesets)
|
private void load(RulesetStore rulesets)
|
||||||
{
|
{
|
||||||
maniaRuleset = rulesets.GetRuleset(3);
|
|
||||||
taikoRuleset = rulesets.GetRuleset(1);
|
taikoRuleset = rulesets.GetRuleset(1);
|
||||||
|
maniaRuleset = rulesets.GetRuleset(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -75,159 +76,53 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
overlay.ShowBeatmapSet(new BeatmapSetInfo
|
overlay.ShowBeatmapSet(new BeatmapSetInfo
|
||||||
{
|
{
|
||||||
|
OnlineBeatmapSetID = 1235,
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
Title = @"Lachryma <Re:Queen’M>",
|
Title = @"an awesome beatmap",
|
||||||
Artist = @"Kaneko Chiharu",
|
Artist = @"naru narusegawa",
|
||||||
Source = @"SOUND VOLTEX III GRAVITY WARS",
|
Source = @"hinata sou",
|
||||||
Tags = @"sdvx grace the 5th kac original song contest konami bemani",
|
Tags = @"test tag tag more tag",
|
||||||
Author = new User
|
Author = new User
|
||||||
{
|
{
|
||||||
Username = @"Fresh Chicken",
|
Username = @"BanchoBot",
|
||||||
Id = 3984370,
|
Id = 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OnlineInfo = new BeatmapSetOnlineInfo
|
OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
{
|
{
|
||||||
Preview = @"https://b.ppy.sh/preview/415886.mp3",
|
Preview = @"https://b.ppy.sh/preview/12345.mp3",
|
||||||
PlayCount = 681380,
|
PlayCount = 123,
|
||||||
FavouriteCount = 356,
|
FavouriteCount = 456,
|
||||||
Submitted = new DateTime(2016, 2, 10),
|
Submitted = DateTime.Now,
|
||||||
Ranked = new DateTime(2016, 6, 19),
|
Ranked = DateTime.Now,
|
||||||
Status = BeatmapSetOnlineStatus.Ranked,
|
BPM = 111,
|
||||||
BPM = 236,
|
|
||||||
HasVideo = true,
|
HasVideo = true,
|
||||||
Covers = new BeatmapSetOnlineCovers
|
HasStoryboard = true,
|
||||||
{
|
Covers = new BeatmapSetOnlineCovers(),
|
||||||
Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
|
||||||
Beatmaps = new List<BeatmapInfo>
|
Beatmaps = new List<BeatmapInfo>
|
||||||
{
|
{
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 1.36,
|
StarDifficulty = 9.99,
|
||||||
Version = @"BASIC",
|
Version = @"TEST",
|
||||||
Ruleset = maniaRuleset,
|
Ruleset = maniaRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 4,
|
CircleSize = 1,
|
||||||
DrainRate = 6.5f,
|
DrainRate = 2.3f,
|
||||||
OverallDifficulty = 6.5f,
|
OverallDifficulty = 4.5f,
|
||||||
ApproachRate = 5,
|
ApproachRate = 6,
|
||||||
},
|
},
|
||||||
OnlineInfo = new BeatmapOnlineInfo
|
OnlineInfo = new BeatmapOnlineInfo
|
||||||
{
|
{
|
||||||
Length = 115000,
|
Length = 456000,
|
||||||
CircleCount = 265,
|
CircleCount = 111,
|
||||||
SliderCount = 71,
|
SliderCount = 12,
|
||||||
PlayCount = 47906,
|
PlayCount = 222,
|
||||||
PassCount = 19899,
|
PassCount = 21,
|
||||||
},
|
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new BeatmapInfo
|
|
||||||
{
|
|
||||||
StarDifficulty = 2.22,
|
|
||||||
Version = @"NOVICE",
|
|
||||||
Ruleset = maniaRuleset,
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 4,
|
|
||||||
DrainRate = 7,
|
|
||||||
OverallDifficulty = 7,
|
|
||||||
ApproachRate = 5,
|
|
||||||
},
|
|
||||||
OnlineInfo = new BeatmapOnlineInfo
|
|
||||||
{
|
|
||||||
Length = 118000,
|
|
||||||
CircleCount = 592,
|
|
||||||
SliderCount = 62,
|
|
||||||
PlayCount = 162021,
|
|
||||||
PassCount = 72116,
|
|
||||||
},
|
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new BeatmapInfo
|
|
||||||
{
|
|
||||||
StarDifficulty = 3.49,
|
|
||||||
Version = @"ADVANCED",
|
|
||||||
Ruleset = maniaRuleset,
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 4,
|
|
||||||
DrainRate = 7.5f,
|
|
||||||
OverallDifficulty = 7.5f,
|
|
||||||
ApproachRate = 5,
|
|
||||||
},
|
|
||||||
OnlineInfo = new BeatmapOnlineInfo
|
|
||||||
{
|
|
||||||
Length = 118000,
|
|
||||||
CircleCount = 1042,
|
|
||||||
SliderCount = 79,
|
|
||||||
PlayCount = 225178,
|
|
||||||
PassCount = 73001,
|
|
||||||
},
|
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new BeatmapInfo
|
|
||||||
{
|
|
||||||
StarDifficulty = 4.24,
|
|
||||||
Version = @"EXHAUST",
|
|
||||||
Ruleset = maniaRuleset,
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 4,
|
|
||||||
DrainRate = 8,
|
|
||||||
OverallDifficulty = 8,
|
|
||||||
ApproachRate = 5,
|
|
||||||
},
|
|
||||||
OnlineInfo = new BeatmapOnlineInfo
|
|
||||||
{
|
|
||||||
Length = 118000,
|
|
||||||
CircleCount = 1352,
|
|
||||||
SliderCount = 69,
|
|
||||||
PlayCount = 131545,
|
|
||||||
PassCount = 42703,
|
|
||||||
},
|
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new BeatmapInfo
|
|
||||||
{
|
|
||||||
StarDifficulty = 5.26,
|
|
||||||
Version = @"GRAVITY",
|
|
||||||
Ruleset = maniaRuleset,
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 4,
|
|
||||||
DrainRate = 8.5f,
|
|
||||||
OverallDifficulty = 8.5f,
|
|
||||||
ApproachRate = 5,
|
|
||||||
},
|
|
||||||
OnlineInfo = new BeatmapOnlineInfo
|
|
||||||
{
|
|
||||||
Length = 118000,
|
|
||||||
CircleCount = 1730,
|
|
||||||
SliderCount = 115,
|
|
||||||
PlayCount = 117673,
|
|
||||||
PassCount = 24241,
|
|
||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
@ -239,162 +134,68 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep(@"show second", () =>
|
downloadAssert(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAvailability()
|
||||||
|
{
|
||||||
|
AddStep(@"show undownloadable", () =>
|
||||||
{
|
{
|
||||||
overlay.ShowBeatmapSet(new BeatmapSetInfo
|
overlay.ShowBeatmapSet(new BeatmapSetInfo
|
||||||
{
|
{
|
||||||
|
OnlineBeatmapSetID = 1234,
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
Title = @"Soumatou Labyrinth",
|
Title = @"undownloadable beatmap",
|
||||||
Artist = @"Yunomi with Momobako&miko",
|
Artist = @"no one",
|
||||||
Tags = @"mmbk.com yuzu__rinrin charlotte",
|
Source = @"some source",
|
||||||
|
Tags = @"another test tag tag more test tags",
|
||||||
Author = new User
|
Author = new User
|
||||||
{
|
{
|
||||||
Username = @"komasy",
|
Username = @"BanchoBot",
|
||||||
Id = 1980256,
|
Id = 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OnlineInfo = new BeatmapSetOnlineInfo
|
OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
{
|
{
|
||||||
Preview = @"https://b.ppy.sh/preview/625493.mp3",
|
Availability = new BeatmapSetOnlineAvailability
|
||||||
PlayCount = 22996,
|
|
||||||
FavouriteCount = 58,
|
|
||||||
Submitted = new DateTime(2016, 6, 11),
|
|
||||||
Ranked = new DateTime(2016, 7, 12),
|
|
||||||
Status = BeatmapSetOnlineStatus.Pending,
|
|
||||||
BPM = 160,
|
|
||||||
HasVideo = false,
|
|
||||||
Covers = new BeatmapSetOnlineCovers
|
|
||||||
{
|
{
|
||||||
Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472",
|
DownloadDisabled = true,
|
||||||
|
ExternalLink = "https://osu.ppy.sh",
|
||||||
},
|
},
|
||||||
|
Preview = @"https://b.ppy.sh/preview/1234.mp3",
|
||||||
|
PlayCount = 123,
|
||||||
|
FavouriteCount = 456,
|
||||||
|
Submitted = DateTime.Now,
|
||||||
|
Ranked = DateTime.Now,
|
||||||
|
BPM = 111,
|
||||||
|
HasVideo = true,
|
||||||
|
HasStoryboard = true,
|
||||||
|
Covers = new BeatmapSetOnlineCovers(),
|
||||||
},
|
},
|
||||||
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
|
||||||
Beatmaps = new List<BeatmapInfo>
|
Beatmaps = new List<BeatmapInfo>
|
||||||
{
|
{
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 1.40,
|
StarDifficulty = 5.67,
|
||||||
Version = @"yzrin's Kantan",
|
Version = @"ANOTHER TEST",
|
||||||
Ruleset = taikoRuleset,
|
Ruleset = taikoRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 2,
|
CircleSize = 9,
|
||||||
DrainRate = 7,
|
DrainRate = 8,
|
||||||
OverallDifficulty = 3,
|
OverallDifficulty = 7,
|
||||||
ApproachRate = 10,
|
ApproachRate = 6,
|
||||||
},
|
},
|
||||||
OnlineInfo = new BeatmapOnlineInfo
|
OnlineInfo = new BeatmapOnlineInfo
|
||||||
{
|
{
|
||||||
Length = 193000,
|
Length = 123000,
|
||||||
CircleCount = 262,
|
CircleCount = 123,
|
||||||
SliderCount = 0,
|
SliderCount = 45,
|
||||||
PlayCount = 3952,
|
PlayCount = 567,
|
||||||
PassCount = 1373,
|
PassCount = 89,
|
||||||
},
|
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new BeatmapInfo
|
|
||||||
{
|
|
||||||
StarDifficulty = 2.23,
|
|
||||||
Version = @"Futsuu",
|
|
||||||
Ruleset = taikoRuleset,
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 2,
|
|
||||||
DrainRate = 6,
|
|
||||||
OverallDifficulty = 4,
|
|
||||||
ApproachRate = 10,
|
|
||||||
},
|
|
||||||
OnlineInfo = new BeatmapOnlineInfo
|
|
||||||
{
|
|
||||||
Length = 193000,
|
|
||||||
CircleCount = 464,
|
|
||||||
SliderCount = 0,
|
|
||||||
PlayCount = 4833,
|
|
||||||
PassCount = 920,
|
|
||||||
},
|
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new BeatmapInfo
|
|
||||||
{
|
|
||||||
StarDifficulty = 3.19,
|
|
||||||
Version = @"Muzukashii",
|
|
||||||
Ruleset = taikoRuleset,
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 2,
|
|
||||||
DrainRate = 6,
|
|
||||||
OverallDifficulty = 5,
|
|
||||||
ApproachRate = 10,
|
|
||||||
},
|
|
||||||
OnlineInfo = new BeatmapOnlineInfo
|
|
||||||
{
|
|
||||||
Length = 193000,
|
|
||||||
CircleCount = 712,
|
|
||||||
SliderCount = 0,
|
|
||||||
PlayCount = 4405,
|
|
||||||
PassCount = 854,
|
|
||||||
},
|
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new BeatmapInfo
|
|
||||||
{
|
|
||||||
StarDifficulty = 3.97,
|
|
||||||
Version = @"Charlotte's Oni",
|
|
||||||
Ruleset = taikoRuleset,
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 5,
|
|
||||||
DrainRate = 6,
|
|
||||||
OverallDifficulty = 5.5f,
|
|
||||||
ApproachRate = 10,
|
|
||||||
},
|
|
||||||
OnlineInfo = new BeatmapOnlineInfo
|
|
||||||
{
|
|
||||||
Length = 193000,
|
|
||||||
CircleCount = 943,
|
|
||||||
SliderCount = 0,
|
|
||||||
PlayCount = 3950,
|
|
||||||
PassCount = 693,
|
|
||||||
},
|
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new BeatmapInfo
|
|
||||||
{
|
|
||||||
StarDifficulty = 5.08,
|
|
||||||
Version = @"Labyrinth Oni",
|
|
||||||
Ruleset = taikoRuleset,
|
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
|
||||||
{
|
|
||||||
CircleSize = 5,
|
|
||||||
DrainRate = 5,
|
|
||||||
OverallDifficulty = 6,
|
|
||||||
ApproachRate = 10,
|
|
||||||
},
|
|
||||||
OnlineInfo = new BeatmapOnlineInfo
|
|
||||||
{
|
|
||||||
Length = 193000,
|
|
||||||
CircleCount = 1068,
|
|
||||||
SliderCount = 0,
|
|
||||||
PlayCount = 5856,
|
|
||||||
PassCount = 1207,
|
|
||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
@ -405,6 +206,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
downloadAssert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -418,5 +221,15 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
AddStep(@"show without reload", overlay.Show);
|
AddStep(@"show without reload", overlay.Show);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void downloadAssert(bool shown)
|
||||||
|
{
|
||||||
|
AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.DownloadButtonsVisible == shown);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestBeatmapSetOverlay : BeatmapSetOverlay
|
||||||
|
{
|
||||||
|
public bool DownloadButtonsVisible => Header.DownloadButtonsVisible;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Overlays.Direct;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public class TestSceneDirectDownloadButton : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(DownloadButton)
|
||||||
|
};
|
||||||
|
|
||||||
|
private TestDownloadButton downloadButton;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDownloadableBeatmap()
|
||||||
|
{
|
||||||
|
createButton(true);
|
||||||
|
assertEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUndownloadableBeatmap()
|
||||||
|
{
|
||||||
|
createButton(false);
|
||||||
|
assertEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
AddAssert($"button {(enabled ? "enabled" : "disabled")}", () => downloadButton.DownloadEnabled == enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createButton(bool downloadable)
|
||||||
|
{
|
||||||
|
AddStep("create button", () =>
|
||||||
|
{
|
||||||
|
Child = downloadButton = new TestDownloadButton(downloadable ? getDownloadableBeatmapSet() : getUndownloadableBeatmapSet())
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(75, 50),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeatmapSetInfo getDownloadableBeatmapSet()
|
||||||
|
{
|
||||||
|
var normal = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo;
|
||||||
|
normal.OnlineInfo.HasVideo = true;
|
||||||
|
normal.OnlineInfo.HasStoryboard = true;
|
||||||
|
|
||||||
|
return normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeatmapSetInfo getUndownloadableBeatmapSet()
|
||||||
|
{
|
||||||
|
var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo;
|
||||||
|
beatmap.Metadata.Artist = "test";
|
||||||
|
beatmap.Metadata.Title = "undownloadable";
|
||||||
|
beatmap.Metadata.AuthorString = "test";
|
||||||
|
|
||||||
|
beatmap.OnlineInfo.HasVideo = true;
|
||||||
|
beatmap.OnlineInfo.HasStoryboard = true;
|
||||||
|
|
||||||
|
beatmap.OnlineInfo.Availability = new BeatmapSetOnlineAvailability
|
||||||
|
{
|
||||||
|
DownloadDisabled = true,
|
||||||
|
ExternalLink = "http://osu.ppy.sh",
|
||||||
|
};
|
||||||
|
|
||||||
|
return beatmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestDownloadButton : DownloadButton
|
||||||
|
{
|
||||||
|
public new bool DownloadEnabled => base.DownloadEnabled;
|
||||||
|
|
||||||
|
public TestDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
|
||||||
|
: base(beatmapSet, noVideo)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,8 +6,11 @@ using System.Collections.Generic;
|
|||||||
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;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Overlays.Direct;
|
using osu.Game.Overlays.Direct;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
namespace osu.Game.Tests.Visual.Online
|
||||||
@ -21,25 +24,74 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
typeof(IconPill)
|
typeof(IconPill)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private BeatmapSetInfo getUndownloadableBeatmapSet(RulesetInfo ruleset) => new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
OnlineBeatmapSetID = 123,
|
||||||
|
Metadata = new BeatmapMetadata
|
||||||
|
{
|
||||||
|
Title = "undownloadable beatmap",
|
||||||
|
Artist = "test",
|
||||||
|
Source = "more tests",
|
||||||
|
Author = new User
|
||||||
|
{
|
||||||
|
Username = "BanchoBot",
|
||||||
|
Id = 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
|
{
|
||||||
|
Availability = new BeatmapSetOnlineAvailability
|
||||||
|
{
|
||||||
|
DownloadDisabled = true,
|
||||||
|
},
|
||||||
|
Preview = @"https://b.ppy.sh/preview/12345.mp3",
|
||||||
|
PlayCount = 123,
|
||||||
|
FavouriteCount = 456,
|
||||||
|
BPM = 111,
|
||||||
|
HasVideo = true,
|
||||||
|
HasStoryboard = true,
|
||||||
|
Covers = new BeatmapSetOnlineCovers(),
|
||||||
|
},
|
||||||
|
Beatmaps = new List<BeatmapInfo>
|
||||||
|
{
|
||||||
|
new BeatmapInfo
|
||||||
|
{
|
||||||
|
Ruleset = ruleset,
|
||||||
|
Version = "Test",
|
||||||
|
StarDifficulty = 6.42,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
var ruleset = new OsuRuleset().RulesetInfo;
|
||||||
beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true;
|
|
||||||
beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true;
|
|
||||||
|
|
||||||
Child = new FillFlowContainer
|
var normal = CreateWorkingBeatmap(ruleset).BeatmapSetInfo;
|
||||||
|
normal.OnlineInfo.HasVideo = true;
|
||||||
|
normal.OnlineInfo.HasStoryboard = true;
|
||||||
|
|
||||||
|
var undownloadable = getUndownloadableBeatmapSet(ruleset);
|
||||||
|
|
||||||
|
Child = new BasicScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.Both,
|
||||||
AutoSizeAxes = Axes.Y,
|
Child = new FillFlowContainer
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Padding = new MarginPadding(20),
|
|
||||||
Spacing = new Vector2(0, 20),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new DirectGridPanel(beatmap.BeatmapSetInfo),
|
RelativeSizeAxes = Axes.X,
|
||||||
new DirectListPanel(beatmap.BeatmapSetInfo)
|
AutoSizeAxes = Axes.Y,
|
||||||
}
|
Direction = FillDirection.Vertical,
|
||||||
|
Padding = new MarginPadding(20),
|
||||||
|
Spacing = new Vector2(0, 20),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new DirectGridPanel(normal),
|
||||||
|
new DirectListPanel(normal),
|
||||||
|
new DirectGridPanel(undownloadable),
|
||||||
|
new DirectListPanel(undownloadable),
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Overlays.Profile.Header.Components;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Rulesets.Catch;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Taiko;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public class TestSceneProfileRulesetSelector : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(ProfileRulesetSelector),
|
||||||
|
typeof(ProfileRulesetTabItem),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestSceneProfileRulesetSelector()
|
||||||
|
{
|
||||||
|
ProfileRulesetSelector selector;
|
||||||
|
|
||||||
|
Child = selector = new ProfileRulesetSelector
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
};
|
||||||
|
|
||||||
|
AddStep("set osu! as default", () => selector.SetDefaultRuleset(new OsuRuleset().RulesetInfo));
|
||||||
|
AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo));
|
||||||
|
AddStep("set taiko as default", () => selector.SetDefaultRuleset(new TaikoRuleset().RulesetInfo));
|
||||||
|
AddStep("set catch as default", () => selector.SetDefaultRuleset(new CatchRuleset().RulesetInfo));
|
||||||
|
AddStep("select default ruleset", selector.SelectDefaultRuleset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs
Normal file
52
osu.Game.Tests/Visual/UserInterface/TestSceneBackButton.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
public class TestSceneBackButton : OsuTestScene
|
||||||
|
{
|
||||||
|
private readonly BackButton button;
|
||||||
|
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(TwoLayerButton)
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestSceneBackButton()
|
||||||
|
{
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(300),
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.SlateGray
|
||||||
|
},
|
||||||
|
button = new BackButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Action = () => button.Hide(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AddStep("show button", () => button.Show());
|
||||||
|
AddStep("hide button", () => button.Hide());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
osu.Game.Tests/Visual/UserInterface/TestSceneNumberBox.cs
Normal file
55
osu.Game.Tests/Visual/UserInterface/TestSceneNumberBox.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneNumberBox : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(OsuNumberBox),
|
||||||
|
};
|
||||||
|
|
||||||
|
private OsuNumberBox numberBox;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Padding = new MarginPadding { Horizontal = 250 },
|
||||||
|
Child = numberBox = new OsuNumberBox
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
PlaceholderText = "Insert numbers here"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
clearInput();
|
||||||
|
AddStep("enter numbers", () => numberBox.Text = "987654321");
|
||||||
|
expectedValue("987654321");
|
||||||
|
clearInput();
|
||||||
|
AddStep("enter text + single number", () => numberBox.Text = "1 hello 2 world 3");
|
||||||
|
expectedValue("123");
|
||||||
|
clearInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearInput() => AddStep("clear input", () => numberBox.Text = null);
|
||||||
|
|
||||||
|
private void expectedValue(string value) => AddAssert("expect number", () => numberBox.Text == value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Overlays.Toolbar;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
public class TestSceneToolbarRulesetSelector : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(ToolbarRulesetSelector),
|
||||||
|
typeof(ToolbarRulesetTabButton),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestSceneToolbarRulesetSelector()
|
||||||
|
{
|
||||||
|
ToolbarRulesetSelector selector;
|
||||||
|
|
||||||
|
Add(new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Height = Toolbar.HEIGHT,
|
||||||
|
Child = selector = new ToolbarRulesetSelector()
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("Select random", () =>
|
||||||
|
{
|
||||||
|
selector.Current.Value = selector.Items.ElementAt(RNG.Next(selector.Items.Count()));
|
||||||
|
});
|
||||||
|
AddStep("Toggle disabled state", () => selector.Current.Disabled = !selector.Current.Disabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +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 System.ComponentModel;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.UserInterface
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
{
|
{
|
||||||
[Description("mostly back button")]
|
|
||||||
public class TestSceneTwoLayerButton : OsuTestScene
|
public class TestSceneTwoLayerButton : OsuTestScene
|
||||||
{
|
{
|
||||||
public TestSceneTwoLayerButton()
|
public TestSceneTwoLayerButton()
|
||||||
{
|
{
|
||||||
Add(new BackButton());
|
Add(new TwoLayerButton
|
||||||
|
{
|
||||||
|
Position = new Vector2(100),
|
||||||
|
Text = "button",
|
||||||
|
Icon = FontAwesome.Solid.Check,
|
||||||
|
BackgroundColour = Color4.SlateGray,
|
||||||
|
HoverColour = Color4.SlateGray.Darken(0.2f)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SettingsTextBox
|
new SettingsNumberBox
|
||||||
{
|
{
|
||||||
LabelText = "Beatmap ID",
|
LabelText = "Beatmap ID",
|
||||||
RelativeSizeAxes = Axes.None,
|
RelativeSizeAxes = Axes.None,
|
||||||
|
@ -231,7 +231,7 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SettingsTextBox
|
new SettingsNumberBox
|
||||||
{
|
{
|
||||||
LabelText = "User ID",
|
LabelText = "User ID",
|
||||||
RelativeSizeAxes = Axes.None,
|
RelativeSizeAxes = Axes.None,
|
||||||
|
@ -5,7 +5,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
@ -191,8 +190,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
|||||||
|
|
||||||
public RoundDisplay()
|
public RoundDisplay()
|
||||||
{
|
{
|
||||||
CornerRadius = 10;
|
|
||||||
Masking = true;
|
|
||||||
Width = 200;
|
Width = 200;
|
||||||
Height = 20;
|
Height = 20;
|
||||||
}
|
}
|
||||||
@ -208,11 +205,6 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
|||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Colour = new Color4(47, 71, 67, 255),
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
|
@ -51,8 +51,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
|||||||
name = round.Name.GetBoundCopy();
|
name = round.Name.GetBoundCopy();
|
||||||
name.BindValueChanged(n => textName.Text = ((losers ? "Losers " : "") + round.Name).ToUpper(), true);
|
name.BindValueChanged(n => textName.Text = ((losers ? "Losers " : "") + round.Name).ToUpper(), true);
|
||||||
|
|
||||||
description = round.Name.GetBoundCopy();
|
description = round.Description.GetBoundCopy();
|
||||||
description.BindValueChanged(n => textDescription.Text = round.Description.Value.ToUpper(), true);
|
description.BindValueChanged(n => textDescription.Text = round.Description.Value?.ToUpper(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,25 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Tournament.Screens.Showcase
|
namespace osu.Game.Tournament.Screens.Showcase
|
||||||
{
|
{
|
||||||
public class TournamentLogo : CompositeDrawable
|
public class TournamentLogo : CompositeDrawable
|
||||||
{
|
{
|
||||||
public TournamentLogo()
|
public TournamentLogo(bool includeRoundBackground = true)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = 95;
|
|
||||||
Margin = new MarginPadding { Vertical = 5 };
|
Margin = new MarginPadding { Vertical = 5 };
|
||||||
|
|
||||||
|
if (includeRoundBackground)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Masking = true;
|
||||||
|
Height = 100;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -27,9 +35,6 @@ namespace osu.Game.Tournament.Screens.Showcase
|
|||||||
Texture = textures.Get("game-screen-logo"),
|
Texture = textures.Get("game-screen-logo"),
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
FillMode = FillMode.Fit,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Size = Vector2.One
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Loop = true,
|
Loop = true,
|
||||||
},
|
},
|
||||||
new TournamentLogo(),
|
new TournamentLogo(false),
|
||||||
mainContainer = new Container
|
mainContainer = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
@ -222,6 +222,7 @@ namespace osu.Game.Tournament
|
|||||||
sw.Write(JsonConvert.SerializeObject(ladder,
|
sw.Write(JsonConvert.SerializeObject(ladder,
|
||||||
new JsonSerializerSettings
|
new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
|
Formatting = Formatting.Indented,
|
||||||
NullValueHandling = NullValueHandling.Ignore,
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
DefaultValueHandling = DefaultValueHandling.Ignore,
|
DefaultValueHandling = DefaultValueHandling.Ignore,
|
||||||
}));
|
}));
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Configuration;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -26,7 +25,7 @@ namespace osu.Game.Audio
|
|||||||
private TrackManagerPreviewTrack current;
|
private TrackManagerPreviewTrack current;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio, FrameworkConfigManager config)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
// this is a temporary solution to get around muting ourselves.
|
// this is a temporary solution to get around muting ourselves.
|
||||||
// todo: update this once we have a BackgroundTrackManager or similar.
|
// todo: update this once we have a BackgroundTrackManager or similar.
|
||||||
@ -36,8 +35,6 @@ namespace osu.Game.Audio
|
|||||||
trackStore.AddAdjustment(AdjustableProperty.Volume, audio.VolumeTrack);
|
trackStore.AddAdjustment(AdjustableProperty.Volume, audio.VolumeTrack);
|
||||||
|
|
||||||
this.audio = audio;
|
this.audio = audio;
|
||||||
|
|
||||||
config.BindWith(FrameworkSetting.VolumeMusic, trackStore.Volume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -21,7 +21,6 @@ using osu.Game.Database;
|
|||||||
using osu.Game.IO.Archives;
|
using osu.Game.IO.Archives;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Overlays.Notifications;
|
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
@ -29,7 +28,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps.
|
/// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class BeatmapManager : ArchiveModelManager<BeatmapSetInfo, BeatmapSetFileInfo>
|
public partial class BeatmapManager : DownloadableArchiveModelManager<BeatmapSetInfo, BeatmapSetFileInfo>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when a single difficulty has been hidden.
|
/// Fired when a single difficulty has been hidden.
|
||||||
@ -41,16 +40,6 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<BeatmapInfo> BeatmapRestored;
|
public event Action<BeatmapInfo> BeatmapRestored;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fired when a beatmap download begins.
|
|
||||||
/// </summary>
|
|
||||||
public event Action<DownloadBeatmapSetRequest> BeatmapDownloadBegan;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fired when a beatmap download is interrupted, due to user cancellation or other failures.
|
|
||||||
/// </summary>
|
|
||||||
public event Action<DownloadBeatmapSetRequest> BeatmapDownloadFailed;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
|
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -66,22 +55,17 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
private readonly BeatmapStore beatmaps;
|
private readonly BeatmapStore beatmaps;
|
||||||
|
|
||||||
private readonly IAPIProvider api;
|
|
||||||
|
|
||||||
private readonly AudioManager audioManager;
|
private readonly AudioManager audioManager;
|
||||||
|
|
||||||
private readonly GameHost host;
|
private readonly GameHost host;
|
||||||
|
|
||||||
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
|
|
||||||
|
|
||||||
private readonly BeatmapUpdateQueue updateQueue;
|
private readonly BeatmapUpdateQueue updateQueue;
|
||||||
|
|
||||||
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null,
|
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null,
|
||||||
WorkingBeatmap defaultBeatmap = null)
|
WorkingBeatmap defaultBeatmap = null)
|
||||||
: base(storage, contextFactory, new BeatmapStore(contextFactory), host)
|
: base(storage, contextFactory, api, new BeatmapStore(contextFactory), host)
|
||||||
{
|
{
|
||||||
this.rulesets = rulesets;
|
this.rulesets = rulesets;
|
||||||
this.api = api;
|
|
||||||
this.audioManager = audioManager;
|
this.audioManager = audioManager;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
|
|
||||||
@ -94,6 +78,9 @@ namespace osu.Game.Beatmaps
|
|||||||
updateQueue = new BeatmapUpdateQueue(api);
|
updateQueue = new BeatmapUpdateQueue(api);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ArchiveDownloadRequest<BeatmapSetInfo> CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) =>
|
||||||
|
new DownloadBeatmapSetRequest(set, minimiseDownloadSize);
|
||||||
|
|
||||||
protected override IEnumerable<string> GetStableImportPaths() => GetStableStorage().GetDirectories(ImportFromStablePath);
|
protected override IEnumerable<string> GetStableImportPaths() => GetStableStorage().GetDirectories(ImportFromStablePath);
|
||||||
|
|
||||||
protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
|
protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
|
||||||
@ -160,87 +147,6 @@ namespace osu.Game.Beatmaps
|
|||||||
void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineBeatmapID = null);
|
void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineBeatmapID = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Downloads a beatmap.
|
|
||||||
/// This will post notifications tracking progress.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
|
|
||||||
/// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param>
|
|
||||||
/// <returns>Downloading can happen</returns>
|
|
||||||
public bool Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
|
|
||||||
{
|
|
||||||
var existing = GetExistingDownload(beatmapSetInfo);
|
|
||||||
|
|
||||||
if (existing != null || api == null) return false;
|
|
||||||
|
|
||||||
var downloadNotification = new DownloadNotification
|
|
||||||
{
|
|
||||||
Text = $"Downloading {beatmapSetInfo}",
|
|
||||||
};
|
|
||||||
|
|
||||||
var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);
|
|
||||||
|
|
||||||
request.DownloadProgressed += progress =>
|
|
||||||
{
|
|
||||||
downloadNotification.State = ProgressNotificationState.Active;
|
|
||||||
downloadNotification.Progress = progress;
|
|
||||||
};
|
|
||||||
|
|
||||||
request.Success += filename =>
|
|
||||||
{
|
|
||||||
Task.Factory.StartNew(async () =>
|
|
||||||
{
|
|
||||||
// This gets scheduled back to the update thread, but we want the import to run in the background.
|
|
||||||
await Import(downloadNotification, filename);
|
|
||||||
currentDownloads.Remove(request);
|
|
||||||
}, TaskCreationOptions.LongRunning);
|
|
||||||
};
|
|
||||||
|
|
||||||
request.Failure += error =>
|
|
||||||
{
|
|
||||||
BeatmapDownloadFailed?.Invoke(request);
|
|
||||||
|
|
||||||
if (error is OperationCanceledException) return;
|
|
||||||
|
|
||||||
downloadNotification.State = ProgressNotificationState.Cancelled;
|
|
||||||
Logger.Error(error, "Beatmap download failed!");
|
|
||||||
currentDownloads.Remove(request);
|
|
||||||
};
|
|
||||||
|
|
||||||
downloadNotification.CancelRequested += () =>
|
|
||||||
{
|
|
||||||
request.Cancel();
|
|
||||||
currentDownloads.Remove(request);
|
|
||||||
downloadNotification.State = ProgressNotificationState.Cancelled;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
currentDownloads.Add(request);
|
|
||||||
PostNotification?.Invoke(downloadNotification);
|
|
||||||
|
|
||||||
// don't run in the main api queue as this is a long-running task.
|
|
||||||
Task.Factory.StartNew(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
request.Perform(api);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// no need to handle here as exceptions will filter down to request.Failure above.
|
|
||||||
}
|
|
||||||
}, TaskCreationOptions.LongRunning);
|
|
||||||
BeatmapDownloadBegan?.Invoke(request);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get an existing download request if it exists.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="beatmap">The <see cref="BeatmapSetInfo"/> whose download request is wanted.</param>
|
|
||||||
/// <returns>The <see cref="DownloadBeatmapSetRequest"/> object if it exists, or null.</returns>
|
|
||||||
public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delete a beatmap difficulty.
|
/// Delete a beatmap difficulty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -408,22 +314,6 @@ namespace osu.Game.Beatmaps
|
|||||||
protected override Track GetTrack() => null;
|
protected override Track GetTrack() => null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DownloadNotification : ProgressNotification
|
|
||||||
{
|
|
||||||
public override bool IsImportant => false;
|
|
||||||
|
|
||||||
protected override Notification CreateCompletionNotification() => new SilencedProgressCompletionNotification
|
|
||||||
{
|
|
||||||
Activated = CompletionClickAction,
|
|
||||||
Text = CompletionText
|
|
||||||
};
|
|
||||||
|
|
||||||
private class SilencedProgressCompletionNotification : ProgressCompletionNotification
|
|
||||||
{
|
|
||||||
public override bool IsImportant => false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BeatmapUpdateQueue
|
private class BeatmapUpdateQueue
|
||||||
{
|
{
|
||||||
private readonly IAPIProvider api;
|
private readonly IAPIProvider api;
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Beatmaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)).FileInfo.StoragePath;
|
private string getPathForFile(string filename) => BeatmapSetInfo.Files.FirstOrDefault(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase))?.FileInfo.StoragePath;
|
||||||
|
|
||||||
private TextureStore textureStore;
|
private TextureStore textureStore;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ using osu.Game.Database;
|
|||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles<BeatmapSetFileInfo>, ISoftDelete
|
public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles<BeatmapSetFileInfo>, ISoftDelete, IEquatable<BeatmapSetInfo>
|
||||||
{
|
{
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
|
||||||
@ -49,5 +49,7 @@ namespace osu.Game.Beatmaps
|
|||||||
public override string ToString() => Metadata?.ToString() ?? base.ToString();
|
public override string ToString() => Metadata?.ToString() ?? base.ToString();
|
||||||
|
|
||||||
public bool Protected { get; set; }
|
public bool Protected { get; set; }
|
||||||
|
|
||||||
|
public bool Equals(BeatmapSetInfo other) => OnlineBeatmapSetID == other?.OnlineBeatmapSetID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,11 @@ namespace osu.Game.Beatmaps
|
|||||||
/// The amount of people who have favourited this beatmap set.
|
/// The amount of people who have favourited this beatmap set.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int FavouriteCount { get; set; }
|
public int FavouriteCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The availability of this beatmap set.
|
||||||
|
/// </summary>
|
||||||
|
public BeatmapSetOnlineAvailability Availability { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BeatmapSetOnlineCovers
|
public class BeatmapSetOnlineCovers
|
||||||
@ -84,4 +89,13 @@ namespace osu.Game.Beatmaps
|
|||||||
[JsonProperty(@"list@2x")]
|
[JsonProperty(@"list@2x")]
|
||||||
public string List { get; set; }
|
public string List { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class BeatmapSetOnlineAvailability
|
||||||
|
{
|
||||||
|
[JsonProperty(@"download_disabled")]
|
||||||
|
public bool DownloadDisabled { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(@"more_information")]
|
||||||
|
public string ExternalLink { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
// 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.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osuTK.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables
|
namespace osu.Game.Beatmaps.Drawables
|
||||||
{
|
{
|
||||||
@ -49,7 +50,7 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
Child = new Box
|
Child = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.Black,
|
Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.1f)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,12 +31,10 @@ namespace osu.Game.Database
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TModel">The model type.</typeparam>
|
/// <typeparam name="TModel">The model type.</typeparam>
|
||||||
/// <typeparam name="TFileModel">The associated file join type.</typeparam>
|
/// <typeparam name="TFileModel">The associated file join type.</typeparam>
|
||||||
public abstract class ArchiveModelManager<TModel, TFileModel> : ArchiveModelManager, ICanAcceptFiles
|
public abstract class ArchiveModelManager<TModel, TFileModel> : ArchiveModelManager, ICanAcceptFiles, IModelManager<TModel>
|
||||||
where TModel : class, IHasFiles<TFileModel>, IHasPrimaryKey, ISoftDelete
|
where TModel : class, IHasFiles<TFileModel>, IHasPrimaryKey, ISoftDelete
|
||||||
where TFileModel : INamedFileInfo, new()
|
where TFileModel : INamedFileInfo, new()
|
||||||
{
|
{
|
||||||
public delegate void ItemAddedDelegate(TModel model, bool existing);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set an endpoint for notifications to be posted to.
|
/// Set an endpoint for notifications to be posted to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -46,7 +44,7 @@ namespace osu.Game.Database
|
|||||||
/// Fired when a new <see cref="TModel"/> becomes available in the database.
|
/// Fired when a new <see cref="TModel"/> becomes available in the database.
|
||||||
/// This is not guaranteed to run on the update thread.
|
/// This is not guaranteed to run on the update thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event ItemAddedDelegate ItemAdded;
|
public event Action<TModel> ItemAdded;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when a <see cref="TModel"/> is removed from the database.
|
/// Fired when a <see cref="TModel"/> is removed from the database.
|
||||||
@ -67,56 +65,12 @@ namespace osu.Game.Database
|
|||||||
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
|
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
|
||||||
private ArchiveImportIPCChannel ipc;
|
private ArchiveImportIPCChannel ipc;
|
||||||
|
|
||||||
private readonly List<Action> queuedEvents = new List<Action>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Allows delaying of outwards events until an operation is confirmed (at a database level).
|
|
||||||
/// </summary>
|
|
||||||
private bool delayingEvents;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Begin delaying outwards events.
|
|
||||||
/// </summary>
|
|
||||||
private void delayEvents() => delayingEvents = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Flush delayed events and disable delaying.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="perform">Whether the flushed events should be performed.</param>
|
|
||||||
private void flushEvents(bool perform)
|
|
||||||
{
|
|
||||||
Action[] events;
|
|
||||||
|
|
||||||
lock (queuedEvents)
|
|
||||||
{
|
|
||||||
events = queuedEvents.ToArray();
|
|
||||||
queuedEvents.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (perform)
|
|
||||||
{
|
|
||||||
foreach (var a in events)
|
|
||||||
a.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
delayingEvents = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleEvent(Action a)
|
|
||||||
{
|
|
||||||
if (delayingEvents)
|
|
||||||
lock (queuedEvents)
|
|
||||||
queuedEvents.Add(a);
|
|
||||||
else
|
|
||||||
a.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes<TModel, TFileModel> modelStore, IIpcHost importHost = null)
|
protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes<TModel, TFileModel> modelStore, IIpcHost importHost = null)
|
||||||
{
|
{
|
||||||
ContextFactory = contextFactory;
|
ContextFactory = contextFactory;
|
||||||
|
|
||||||
ModelStore = modelStore;
|
ModelStore = modelStore;
|
||||||
ModelStore.ItemAdded += item => handleEvent(() => ItemAdded?.Invoke(item, false));
|
ModelStore.ItemAdded += item => handleEvent(() => ItemAdded?.Invoke(item));
|
||||||
ModelStore.ItemRemoved += s => handleEvent(() => ItemRemoved?.Invoke(s));
|
ModelStore.ItemRemoved += s => handleEvent(() => ItemRemoved?.Invoke(s));
|
||||||
|
|
||||||
Files = new FileStore(contextFactory, storage);
|
Files = new FileStore(contextFactory, storage);
|
||||||
@ -345,8 +299,6 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
Undelete(existing);
|
Undelete(existing);
|
||||||
LogForModel(item, $"Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import.");
|
LogForModel(item, $"Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import.");
|
||||||
handleEvent(() => ItemAdded?.Invoke(existing, true));
|
|
||||||
|
|
||||||
// existing item will be used; rollback new import and exit early.
|
// existing item will be used; rollback new import and exit early.
|
||||||
rollback();
|
rollback();
|
||||||
flushEvents(true);
|
flushEvents(true);
|
||||||
@ -637,6 +589,54 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
throw new InvalidFormatException($"{path} is not a valid archive");
|
throw new InvalidFormatException($"{path} is not a valid archive");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Event handling / delaying
|
||||||
|
|
||||||
|
private readonly List<Action> queuedEvents = new List<Action>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows delaying of outwards events until an operation is confirmed (at a database level).
|
||||||
|
/// </summary>
|
||||||
|
private bool delayingEvents;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begin delaying outwards events.
|
||||||
|
/// </summary>
|
||||||
|
private void delayEvents() => delayingEvents = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flush delayed events and disable delaying.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="perform">Whether the flushed events should be performed.</param>
|
||||||
|
private void flushEvents(bool perform)
|
||||||
|
{
|
||||||
|
Action[] events;
|
||||||
|
|
||||||
|
lock (queuedEvents)
|
||||||
|
{
|
||||||
|
events = queuedEvents.ToArray();
|
||||||
|
queuedEvents.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (perform)
|
||||||
|
{
|
||||||
|
foreach (var a in events)
|
||||||
|
a.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
delayingEvents = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEvent(Action a)
|
||||||
|
{
|
||||||
|
if (delayingEvents)
|
||||||
|
lock (queuedEvents)
|
||||||
|
queuedEvents.Add(a);
|
||||||
|
else
|
||||||
|
a.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class ArchiveModelManager
|
public abstract class ArchiveModelManager
|
||||||
|
135
osu.Game/Database/DownloadableArchiveModelManager.cs
Normal file
135
osu.Game/Database/DownloadableArchiveModelManager.cs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using Humanizer;
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Overlays.Notifications;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace osu.Game.Database
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="ArchiveModelManager{TModel, TFileModel}"/> that has the ability to download models using an <see cref="IAPIProvider"/> and
|
||||||
|
/// import them into the store.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The model type.</typeparam>
|
||||||
|
/// <typeparam name="TFileModel">The associated file join type.</typeparam>
|
||||||
|
public abstract class DownloadableArchiveModelManager<TModel, TFileModel> : ArchiveModelManager<TModel, TFileModel>, IModelDownloader<TModel>
|
||||||
|
where TModel : class, IHasFiles<TFileModel>, IHasPrimaryKey, ISoftDelete
|
||||||
|
where TFileModel : INamedFileInfo, new()
|
||||||
|
{
|
||||||
|
public event Action<ArchiveDownloadRequest<TModel>> DownloadBegan;
|
||||||
|
|
||||||
|
public event Action<ArchiveDownloadRequest<TModel>> DownloadFailed;
|
||||||
|
|
||||||
|
private readonly IAPIProvider api;
|
||||||
|
|
||||||
|
private readonly List<ArchiveDownloadRequest<TModel>> currentDownloads = new List<ArchiveDownloadRequest<TModel>>();
|
||||||
|
|
||||||
|
private readonly MutableDatabaseBackedStoreWithFileIncludes<TModel, TFileModel> modelStore;
|
||||||
|
|
||||||
|
protected DownloadableArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, IAPIProvider api, MutableDatabaseBackedStoreWithFileIncludes<TModel, TFileModel> modelStore, IIpcHost importHost = null)
|
||||||
|
: base(storage, contextFactory, modelStore, importHost)
|
||||||
|
{
|
||||||
|
this.api = api;
|
||||||
|
this.modelStore = modelStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the download request for this <see cref="TModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The <see cref="TModel"/> to be downloaded.</param>
|
||||||
|
/// <param name="minimiseDownloadSize">Whether this download should be optimised for slow connections. Generally means extras are not included in the download bundle.</param>
|
||||||
|
/// <returns>The request object.</returns>
|
||||||
|
protected abstract ArchiveDownloadRequest<TModel> CreateDownloadRequest(TModel model, bool minimiseDownloadSize);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begin a download for the requested <see cref="TModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The <see cref="TModel"/> to be downloaded.</param>
|
||||||
|
/// <param name="minimiseDownloadSize">Whether this download should be optimised for slow connections. Generally means extras are not included in the download bundle.</param>
|
||||||
|
/// <returns>Whether the download was started.</returns>
|
||||||
|
public bool Download(TModel model, bool minimiseDownloadSize = false)
|
||||||
|
{
|
||||||
|
if (!canDownload(model)) return false;
|
||||||
|
|
||||||
|
var request = CreateDownloadRequest(model, minimiseDownloadSize);
|
||||||
|
|
||||||
|
DownloadNotification notification = new DownloadNotification
|
||||||
|
{
|
||||||
|
Text = $"Downloading {request.Model}",
|
||||||
|
};
|
||||||
|
|
||||||
|
request.DownloadProgressed += progress =>
|
||||||
|
{
|
||||||
|
notification.State = ProgressNotificationState.Active;
|
||||||
|
notification.Progress = progress;
|
||||||
|
};
|
||||||
|
|
||||||
|
request.Success += filename =>
|
||||||
|
{
|
||||||
|
Task.Factory.StartNew(async () =>
|
||||||
|
{
|
||||||
|
// This gets scheduled back to the update thread, but we want the import to run in the background.
|
||||||
|
await Import(notification, filename);
|
||||||
|
currentDownloads.Remove(request);
|
||||||
|
}, TaskCreationOptions.LongRunning);
|
||||||
|
};
|
||||||
|
|
||||||
|
request.Failure += error =>
|
||||||
|
{
|
||||||
|
DownloadFailed?.Invoke(request);
|
||||||
|
|
||||||
|
if (error is OperationCanceledException) return;
|
||||||
|
|
||||||
|
notification.State = ProgressNotificationState.Cancelled;
|
||||||
|
Logger.Error(error, $"{HumanisedModelName.Titleize()} download failed!");
|
||||||
|
currentDownloads.Remove(request);
|
||||||
|
};
|
||||||
|
|
||||||
|
notification.CancelRequested += () =>
|
||||||
|
{
|
||||||
|
request.Cancel();
|
||||||
|
currentDownloads.Remove(request);
|
||||||
|
notification.State = ProgressNotificationState.Cancelled;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
currentDownloads.Add(request);
|
||||||
|
PostNotification?.Invoke(notification);
|
||||||
|
|
||||||
|
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
|
||||||
|
|
||||||
|
DownloadBegan?.Invoke(request);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool IsAvailableLocally(TModel model) => modelStore.ConsumableItems.Any(m => m.Equals(model) && !m.DeletePending);
|
||||||
|
|
||||||
|
public ArchiveDownloadRequest<TModel> GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Model.Equals(model));
|
||||||
|
|
||||||
|
private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null;
|
||||||
|
|
||||||
|
private class DownloadNotification : ProgressNotification
|
||||||
|
{
|
||||||
|
public override bool IsImportant => false;
|
||||||
|
|
||||||
|
protected override Notification CreateCompletionNotification() => new SilencedProgressCompletionNotification
|
||||||
|
{
|
||||||
|
Activated = CompletionClickAction,
|
||||||
|
Text = CompletionText
|
||||||
|
};
|
||||||
|
|
||||||
|
private class SilencedProgressCompletionNotification : ProgressCompletionNotification
|
||||||
|
{
|
||||||
|
public override bool IsImportant => false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
osu.Game/Database/IModelDownloader.cs
Normal file
48
osu.Game/Database/IModelDownloader.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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.Game.Online.API;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace osu.Game.Database
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a <see cref="IModelManager{TModel}"/> that can download new models from an external source.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The model type.</typeparam>
|
||||||
|
public interface IModelDownloader<TModel> : IModelManager<TModel>
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Fired when a <see cref="TModel"/> download begins.
|
||||||
|
/// </summary>
|
||||||
|
event Action<ArchiveDownloadRequest<TModel>> DownloadBegan;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fired when a <see cref="TModel"/> download is interrupted, either due to user cancellation or failure.
|
||||||
|
/// </summary>
|
||||||
|
event Action<ArchiveDownloadRequest<TModel>> DownloadFailed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether a given <see cref="TModel"/> is already available in the local store.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The <see cref="TModel"/> whose existence needs to be checked.</param>
|
||||||
|
/// <returns>Whether the <see cref="TModel"/> exists.</returns>
|
||||||
|
bool IsAvailableLocally(TModel model);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begin a download for the requested <see cref="TModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The <see cref="TModel"/> to be downloaded.</param>
|
||||||
|
/// <param name="minimiseDownloadSize">Whether this download should be optimised for slow connections. Generally means extras are not included in the download bundle..</param>
|
||||||
|
/// <returns>Whether the download was started.</returns>
|
||||||
|
bool Download(TModel model, bool minimiseDownloadSize);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an existing <see cref="TModel"/> download request if it exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The <see cref="TModel"/> whose request is wanted.</param>
|
||||||
|
/// <returns>The <see cref="ArchiveDownloadRequest{TModel}"/> object if it exists, otherwise null.</returns>
|
||||||
|
ArchiveDownloadRequest<TModel> GetExistingDownload(TModel model);
|
||||||
|
}
|
||||||
|
}
|
19
osu.Game/Database/IModelManager.cs
Normal file
19
osu.Game/Database/IModelManager.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace osu.Game.Database
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a model manager that publishes events when <see cref="TModel"/>s are added or removed.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TModel">The model type.</typeparam>
|
||||||
|
public interface IModelManager<out TModel>
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
event Action<TModel> ItemAdded;
|
||||||
|
|
||||||
|
event Action<TModel> ItemRemoved;
|
||||||
|
}
|
||||||
|
}
|
@ -1,26 +1,65 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class BackButton : TwoLayerButton
|
public class BackButton : VisibilityContainer, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
|
public Action Action;
|
||||||
|
|
||||||
|
private readonly TwoLayerButton button;
|
||||||
|
|
||||||
public BackButton()
|
public BackButton()
|
||||||
{
|
{
|
||||||
Text = @"back";
|
Size = TwoLayerButton.SIZE_EXTENDED;
|
||||||
Icon = OsuIcon.LeftCircle;
|
|
||||||
Anchor = Anchor.BottomLeft;
|
Child = button = new TwoLayerButton
|
||||||
Origin = Anchor.BottomLeft;
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Text = @"back",
|
||||||
|
Icon = OsuIcon.LeftCircle,
|
||||||
|
Action = () => Action?.Invoke()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
BackgroundColour = colours.Pink;
|
button.BackgroundColour = colours.Pink;
|
||||||
HoverColour = colours.PinkDark;
|
button.HoverColour = colours.PinkDark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(GlobalAction action)
|
||||||
|
{
|
||||||
|
if (action == GlobalAction.Back)
|
||||||
|
{
|
||||||
|
Action?.Invoke();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(GlobalAction action) => action == GlobalAction.Back;
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
button.MoveToX(0, 400, Easing.OutQuint);
|
||||||
|
button.FadeIn(150, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
button.MoveToX(-TwoLayerButton.SIZE_EXTENDED.X / 2, 400, Easing.OutQuint);
|
||||||
|
button.FadeOut(400, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -97,6 +98,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item) { AccentColour = accentColour };
|
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item) { AccentColour = accentColour };
|
||||||
|
|
||||||
|
protected override ScrollContainer<Drawable> CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction);
|
||||||
|
|
||||||
#region DrawableOsuDropdownMenuItem
|
#region DrawableOsuDropdownMenuItem
|
||||||
|
|
||||||
public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
|
public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
|
||||||
@ -247,8 +250,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Icon = FontAwesome.Solid.ChevronDown,
|
Icon = FontAwesome.Solid.ChevronDown,
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
Margin = new MarginPadding { Right = 4 },
|
Margin = new MarginPadding { Right = 5 },
|
||||||
Size = new Vector2(20),
|
Size = new Vector2(12),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -46,6 +47,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuMenuItem(item);
|
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuMenuItem(item);
|
||||||
|
|
||||||
|
protected override ScrollContainer<Drawable> CreateScrollContainer(Direction direction) => new OsuScrollContainer(direction);
|
||||||
|
|
||||||
protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical)
|
protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical)
|
||||||
{
|
{
|
||||||
Anchor = Direction == Direction.Horizontal ? Anchor.BottomLeft : Anchor.TopRight
|
Anchor = Direction == Direction.Horizontal ? Anchor.BottomLeft : Anchor.TopRight
|
||||||
|
10
osu.Game/Graphics/UserInterface/OsuNumberBox.cs
Normal file
10
osu.Game/Graphics/UserInterface/OsuNumberBox.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
public class OsuNumberBox : OsuTextBox
|
||||||
|
{
|
||||||
|
protected override bool CanAddCharacter(char character) => char.IsNumber(character);
|
||||||
|
}
|
||||||
|
}
|
@ -165,7 +165,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
this.ResizeTo(SIZE_EXTENDED, transform_time, Easing.OutElastic);
|
this.ResizeTo(SIZE_EXTENDED, transform_time, Easing.OutElastic);
|
||||||
IconLayer.FadeColour(HoverColour, transform_time, Easing.OutElastic);
|
|
||||||
|
IconLayer.FadeColour(HoverColour, transform_time / 2f, Easing.OutQuint);
|
||||||
|
|
||||||
bouncingIcon.ScaleTo(1.1f, transform_time, Easing.OutElastic);
|
bouncingIcon.ScaleTo(1.1f, transform_time, Easing.OutElastic);
|
||||||
|
|
||||||
@ -174,16 +175,13 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
protected override void OnHoverLost(HoverLostEvent e)
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
{
|
{
|
||||||
this.ResizeTo(SIZE_RETRACTED, transform_time, Easing.OutElastic);
|
this.ResizeTo(SIZE_RETRACTED, transform_time, Easing.Out);
|
||||||
IconLayer.FadeColour(TextLayer.Colour, transform_time, Easing.OutElastic);
|
IconLayer.FadeColour(TextLayer.Colour, transform_time, Easing.Out);
|
||||||
|
|
||||||
bouncingIcon.ScaleTo(1, transform_time, Easing.OutElastic);
|
bouncingIcon.ScaleTo(1, transform_time, Easing.Out);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e) => true;
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
|
@ -50,6 +50,7 @@ namespace osu.Game.Input.Bindings
|
|||||||
{
|
{
|
||||||
new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene),
|
new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene),
|
||||||
new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry),
|
new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry),
|
||||||
|
new KeyBinding(new[] { InputKey.Control, InputKey.Tilde }, GlobalAction.QuickExit),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.Plus }, GlobalAction.IncreaseScrollSpeed),
|
new KeyBinding(new[] { InputKey.Control, InputKey.Plus }, GlobalAction.IncreaseScrollSpeed),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed),
|
new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed),
|
||||||
};
|
};
|
||||||
@ -111,5 +112,8 @@ namespace osu.Game.Input.Bindings
|
|||||||
|
|
||||||
[Description("Select")]
|
[Description("Select")]
|
||||||
Select,
|
Select,
|
||||||
|
|
||||||
|
[Description("Quick exit (Hold)")]
|
||||||
|
QuickExit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,12 @@ namespace osu.Game.Online.API
|
|||||||
if (checkAndScheduleFailure())
|
if (checkAndScheduleFailure())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
API.Schedule(delegate { Success?.Invoke(); });
|
API.Schedule(delegate
|
||||||
|
{
|
||||||
|
if (cancelled) return;
|
||||||
|
|
||||||
|
Success?.Invoke();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled"));
|
public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled"));
|
||||||
|
24
osu.Game/Online/API/ArchiveDownloadRequest.cs
Normal file
24
osu.Game/Online/API/ArchiveDownloadRequest.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API
|
||||||
|
{
|
||||||
|
public abstract class ArchiveDownloadRequest<TModel> : APIDownloadRequest
|
||||||
|
where TModel : class
|
||||||
|
{
|
||||||
|
public readonly TModel Model;
|
||||||
|
|
||||||
|
public float Progress;
|
||||||
|
|
||||||
|
public event Action<float> DownloadProgressed;
|
||||||
|
|
||||||
|
protected ArchiveDownloadRequest(TModel model)
|
||||||
|
{
|
||||||
|
Model = model;
|
||||||
|
|
||||||
|
Progressed += (current, total) => DownloadProgressed?.Invoke(Progress = (float)current / total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,28 +2,19 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
namespace osu.Game.Online.API.Requests
|
||||||
{
|
{
|
||||||
public class DownloadBeatmapSetRequest : APIDownloadRequest
|
public class DownloadBeatmapSetRequest : ArchiveDownloadRequest<BeatmapSetInfo>
|
||||||
{
|
{
|
||||||
public readonly BeatmapSetInfo BeatmapSet;
|
|
||||||
|
|
||||||
public float Progress;
|
|
||||||
|
|
||||||
public event Action<float> DownloadProgressed;
|
|
||||||
|
|
||||||
private readonly bool noVideo;
|
private readonly bool noVideo;
|
||||||
|
|
||||||
public DownloadBeatmapSetRequest(BeatmapSetInfo set, bool noVideo)
|
public DownloadBeatmapSetRequest(BeatmapSetInfo set, bool noVideo)
|
||||||
|
: base(set)
|
||||||
{
|
{
|
||||||
this.noVideo = noVideo;
|
this.noVideo = noVideo;
|
||||||
BeatmapSet = set;
|
|
||||||
|
|
||||||
Progressed += (current, total) => DownloadProgressed?.Invoke(Progress = (float)current / total);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}";
|
protected override string Target => $@"beatmapsets/{Model.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,9 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
set => Author.Id = value;
|
set => Author.Id = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonProperty(@"availability")]
|
||||||
|
private BeatmapSetOnlineAvailability availability { get; set; }
|
||||||
|
|
||||||
[JsonProperty(@"beatmaps")]
|
[JsonProperty(@"beatmaps")]
|
||||||
private IEnumerable<APIBeatmap> beatmaps { get; set; }
|
private IEnumerable<APIBeatmap> beatmaps { get; set; }
|
||||||
|
|
||||||
@ -87,6 +90,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
Submitted = submitted,
|
Submitted = submitted,
|
||||||
Ranked = ranked,
|
Ranked = ranked,
|
||||||
LastUpdated = lastUpdated,
|
LastUpdated = lastUpdated,
|
||||||
|
Availability = availability,
|
||||||
},
|
},
|
||||||
Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(),
|
Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(),
|
||||||
};
|
};
|
||||||
|
@ -1,7 +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.
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Online
|
||||||
{
|
{
|
||||||
public enum DownloadState
|
public enum DownloadState
|
||||||
{
|
{
|
@ -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.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Database;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Online
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A component which tracks a beatmap through potential download/import/deletion.
|
/// A component which tracks a beatmap through potential download/import/deletion.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DownloadTrackingComposite : CompositeDrawable
|
public abstract class DownloadTrackingComposite<TModel, TModelManager> : CompositeDrawable
|
||||||
|
where TModel : class, IEquatable<TModel>
|
||||||
|
where TModelManager : class, IModelDownloader<TModel>
|
||||||
{
|
{
|
||||||
public readonly Bindable<BeatmapSetInfo> BeatmapSet = new Bindable<BeatmapSetInfo>();
|
protected readonly Bindable<TModel> Model = new Bindable<TModel>();
|
||||||
|
|
||||||
private BeatmapManager beatmaps;
|
private TModelManager manager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded.
|
/// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded.
|
||||||
@ -27,58 +28,39 @@ namespace osu.Game.Overlays.Direct
|
|||||||
|
|
||||||
protected readonly Bindable<double> Progress = new Bindable<double>();
|
protected readonly Bindable<double> Progress = new Bindable<double>();
|
||||||
|
|
||||||
protected DownloadTrackingComposite(BeatmapSetInfo beatmapSet = null)
|
protected DownloadTrackingComposite(TModel model = null)
|
||||||
{
|
{
|
||||||
BeatmapSet.Value = beatmapSet;
|
Model.Value = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(BeatmapManager beatmaps)
|
private void load(TModelManager manager)
|
||||||
{
|
{
|
||||||
this.beatmaps = beatmaps;
|
this.manager = manager;
|
||||||
|
|
||||||
BeatmapSet.BindValueChanged(setInfo =>
|
Model.BindValueChanged(modelInfo =>
|
||||||
{
|
{
|
||||||
if (setInfo.NewValue == null)
|
if (modelInfo.NewValue == null)
|
||||||
attachDownload(null);
|
attachDownload(null);
|
||||||
else if (beatmaps.GetAllUsableBeatmapSetsEnumerable().Any(s => s.OnlineBeatmapSetID == setInfo.NewValue.OnlineBeatmapSetID))
|
else if (manager.IsAvailableLocally(modelInfo.NewValue))
|
||||||
State.Value = DownloadState.LocallyAvailable;
|
State.Value = DownloadState.LocallyAvailable;
|
||||||
else
|
else
|
||||||
attachDownload(beatmaps.GetExistingDownload(setInfo.NewValue));
|
attachDownload(manager.GetExistingDownload(modelInfo.NewValue));
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
beatmaps.BeatmapDownloadBegan += download =>
|
manager.DownloadBegan += download =>
|
||||||
{
|
{
|
||||||
if (download.BeatmapSet.OnlineBeatmapSetID == BeatmapSet.Value?.OnlineBeatmapSetID)
|
if (download.Model.Equals(Model.Value))
|
||||||
attachDownload(download);
|
attachDownload(download);
|
||||||
};
|
};
|
||||||
|
|
||||||
beatmaps.ItemAdded += setAdded;
|
manager.ItemAdded += itemAdded;
|
||||||
beatmaps.ItemRemoved += setRemoved;
|
manager.ItemRemoved += itemRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Disposal
|
private ArchiveDownloadRequest<TModel> attachedRequest;
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
private void attachDownload(ArchiveDownloadRequest<TModel> request)
|
||||||
{
|
|
||||||
base.Dispose(isDisposing);
|
|
||||||
|
|
||||||
if (beatmaps != null)
|
|
||||||
{
|
|
||||||
beatmaps.BeatmapDownloadBegan -= attachDownload;
|
|
||||||
beatmaps.ItemAdded -= setAdded;
|
|
||||||
}
|
|
||||||
|
|
||||||
State.UnbindAll();
|
|
||||||
|
|
||||||
attachDownload(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private DownloadBeatmapSetRequest attachedRequest;
|
|
||||||
|
|
||||||
private void attachDownload(DownloadBeatmapSetRequest request)
|
|
||||||
{
|
{
|
||||||
if (attachedRequest != null)
|
if (attachedRequest != null)
|
||||||
{
|
{
|
||||||
@ -118,16 +100,35 @@ namespace osu.Game.Overlays.Direct
|
|||||||
|
|
||||||
private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null));
|
private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null));
|
||||||
|
|
||||||
private void setAdded(BeatmapSetInfo s, bool existing) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable);
|
private void itemAdded(TModel s) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable);
|
||||||
|
|
||||||
private void setRemoved(BeatmapSetInfo s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded);
|
private void itemRemoved(TModel s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded);
|
||||||
|
|
||||||
private void setDownloadStateFromManager(BeatmapSetInfo s, DownloadState state) => Schedule(() =>
|
private void setDownloadStateFromManager(TModel s, DownloadState state) => Schedule(() =>
|
||||||
{
|
{
|
||||||
if (s.OnlineBeatmapSetID != BeatmapSet.Value?.OnlineBeatmapSetID)
|
if (!s.Equals(Model.Value))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
State.Value = state;
|
State.Value = state;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#region Disposal
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (manager != null)
|
||||||
|
{
|
||||||
|
manager.DownloadBegan -= attachDownload;
|
||||||
|
manager.ItemAdded -= itemAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
State.UnbindAll();
|
||||||
|
|
||||||
|
attachDownload(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -29,6 +29,7 @@ using osu.Framework.Threading;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input;
|
using osu.Game.Input;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -82,6 +83,7 @@ namespace osu.Game
|
|||||||
private OsuScreenStack screenStack;
|
private OsuScreenStack screenStack;
|
||||||
private VolumeOverlay volume;
|
private VolumeOverlay volume;
|
||||||
private OsuLogo osuLogo;
|
private OsuLogo osuLogo;
|
||||||
|
private BackButton backButton;
|
||||||
|
|
||||||
private MainMenu menuScreen;
|
private MainMenu menuScreen;
|
||||||
private Intro introScreen;
|
private Intro introScreen;
|
||||||
@ -400,6 +402,16 @@ namespace osu.Game
|
|||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both },
|
screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both },
|
||||||
|
backButton = new BackButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Action = () =>
|
||||||
|
{
|
||||||
|
if ((screenStack.CurrentScreen as IOsuScreen)?.AllowBackButton == true)
|
||||||
|
screenStack.Exit();
|
||||||
|
}
|
||||||
|
},
|
||||||
logoContainer = new Container { RelativeSizeAxes = Axes.Both },
|
logoContainer = new Container { RelativeSizeAxes = Axes.Both },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -796,6 +808,11 @@ namespace osu.Game
|
|||||||
CloseAllOverlays();
|
CloseAllOverlays();
|
||||||
else
|
else
|
||||||
Toolbar.Show();
|
Toolbar.Show();
|
||||||
|
|
||||||
|
if (newOsuScreen.AllowBackButton)
|
||||||
|
backButton.Show();
|
||||||
|
else
|
||||||
|
backButton.Hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
|
|
||||||
BeatmapManager.ItemRemoved += i => ScoreManager.Delete(getBeatmapScores(i), true);
|
BeatmapManager.ItemRemoved += i => ScoreManager.Delete(getBeatmapScores(i), true);
|
||||||
BeatmapManager.ItemAdded += (i, existing) => ScoreManager.Undelete(getBeatmapScores(i), true);
|
BeatmapManager.ItemAdded += i => ScoreManager.Undelete(getBeatmapScores(i), true);
|
||||||
|
|
||||||
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
|
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
|
||||||
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
|
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
|
||||||
|
83
osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs
Normal file
83
osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs
Normal file
@ -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 osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.BeatmapSet
|
||||||
|
{
|
||||||
|
public class BeatmapAvailability : Container
|
||||||
|
{
|
||||||
|
private BeatmapSetInfo beatmapSet;
|
||||||
|
|
||||||
|
private bool downloadDisabled => BeatmapSet?.OnlineInfo.Availability?.DownloadDisabled ?? false;
|
||||||
|
private bool hasExternalLink => !string.IsNullOrEmpty(BeatmapSet?.OnlineInfo.Availability?.ExternalLink);
|
||||||
|
|
||||||
|
private readonly LinkFlowContainer textContainer;
|
||||||
|
|
||||||
|
public BeatmapAvailability()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
Padding = new MarginPadding { Top = 10, Right = 20 };
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Black.Opacity(0.6f),
|
||||||
|
},
|
||||||
|
textContainer = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 14))
|
||||||
|
{
|
||||||
|
Direction = FillDirection.Full,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding(10),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeatmapSetInfo BeatmapSet
|
||||||
|
{
|
||||||
|
get => beatmapSet;
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == beatmapSet)
|
||||||
|
return;
|
||||||
|
|
||||||
|
beatmapSet = value;
|
||||||
|
|
||||||
|
if (downloadDisabled || hasExternalLink)
|
||||||
|
{
|
||||||
|
Show();
|
||||||
|
updateText();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateText()
|
||||||
|
{
|
||||||
|
textContainer.Clear();
|
||||||
|
textContainer.AddParagraph(downloadDisabled
|
||||||
|
? "This beatmap is currently not available for download."
|
||||||
|
: "Portions of this beatmap have been removed at the request of the creator or a third-party rights holder.", t => t.Colour = Color4.Orange);
|
||||||
|
|
||||||
|
if (hasExternalLink)
|
||||||
|
{
|
||||||
|
textContainer.NewParagraph();
|
||||||
|
textContainer.NewParagraph();
|
||||||
|
textContainer.AddLink("Check here for more information.", BeatmapSet.OnlineInfo.Availability.ExternalLink, creationParameters: t => t.Font = OsuFont.GetFont(size: 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Overlays.Direct;
|
using osu.Game.Overlays.Direct;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -19,7 +20,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.BeatmapSet.Buttons
|
namespace osu.Game.Overlays.BeatmapSet.Buttons
|
||||||
{
|
{
|
||||||
public class DownloadButton : DownloadTrackingComposite, IHasTooltip
|
public class DownloadButton : BeatmapDownloadTrackingComposite, IHasTooltip
|
||||||
{
|
{
|
||||||
private readonly bool noVideo;
|
private readonly bool noVideo;
|
||||||
|
|
||||||
|
@ -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 System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -8,11 +9,11 @@ using osu.Framework.Graphics.Colour;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Beatmaps.Drawables;
|
using osu.Game.Beatmaps.Drawables;
|
||||||
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.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online;
|
||||||
using osu.Game.Overlays.BeatmapSet.Buttons;
|
using osu.Game.Overlays.BeatmapSet.Buttons;
|
||||||
using osu.Game.Overlays.Direct;
|
using osu.Game.Overlays.Direct;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -21,7 +22,7 @@ using DownloadButton = osu.Game.Overlays.BeatmapSet.Buttons.DownloadButton;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.BeatmapSet
|
namespace osu.Game.Overlays.BeatmapSet
|
||||||
{
|
{
|
||||||
public class Header : DownloadTrackingComposite
|
public class Header : BeatmapDownloadTrackingComposite
|
||||||
{
|
{
|
||||||
private const float transition_duration = 200;
|
private const float transition_duration = 200;
|
||||||
private const float tabs_height = 50;
|
private const float tabs_height = 50;
|
||||||
@ -33,13 +34,20 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
private readonly OsuSpriteText title, artist;
|
private readonly OsuSpriteText title, artist;
|
||||||
private readonly AuthorInfo author;
|
private readonly AuthorInfo author;
|
||||||
private readonly FillFlowContainer downloadButtonsContainer;
|
private readonly FillFlowContainer downloadButtonsContainer;
|
||||||
|
private readonly BeatmapAvailability beatmapAvailability;
|
||||||
private readonly BeatmapSetOnlineStatusPill onlineStatusPill;
|
private readonly BeatmapSetOnlineStatusPill onlineStatusPill;
|
||||||
public Details Details;
|
public Details Details;
|
||||||
|
|
||||||
|
public bool DownloadButtonsVisible => downloadButtonsContainer.Any();
|
||||||
|
|
||||||
public readonly BeatmapPicker Picker;
|
public readonly BeatmapPicker Picker;
|
||||||
|
|
||||||
private readonly FavouriteButton favouriteButton;
|
private readonly FavouriteButton favouriteButton;
|
||||||
|
|
||||||
|
private readonly FillFlowContainer fadeContent;
|
||||||
|
|
||||||
|
private readonly LoadingAnimation loading;
|
||||||
|
|
||||||
public Header()
|
public Header()
|
||||||
{
|
{
|
||||||
ExternalLinkButton externalLink;
|
ExternalLinkButton externalLink;
|
||||||
@ -105,63 +113,73 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
Left = BeatmapSetOverlay.X_PADDING,
|
Left = BeatmapSetOverlay.X_PADDING,
|
||||||
Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH,
|
Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH,
|
||||||
},
|
},
|
||||||
Child = new FillFlowContainer
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
fadeContent = new FillFlowContainer
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new Container
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
new Container
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Child = Picker = new BeatmapPicker(),
|
|
||||||
},
|
|
||||||
new FillFlowContainer
|
|
||||||
{
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
title = new OsuSpriteText
|
RelativeSizeAxes = Axes.X,
|
||||||
{
|
AutoSizeAxes = Axes.Y,
|
||||||
Font = OsuFont.GetFont(size: 37, weight: FontWeight.Bold, italics: true)
|
Child = Picker = new BeatmapPicker(),
|
||||||
},
|
},
|
||||||
externalLink = new ExternalLinkButton
|
new FillFlowContainer
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Margin = new MarginPadding { Left = 3, Bottom = 4 }, //To better lineup with the font
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 25, weight: FontWeight.SemiBold, italics: true) },
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Margin = new MarginPadding { Top = 20 },
|
|
||||||
Child = author = new AuthorInfo(),
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = buttons_height,
|
|
||||||
Margin = new MarginPadding { Top = 10 },
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
favouriteButton = new FavouriteButton(),
|
Direction = FillDirection.Horizontal,
|
||||||
downloadButtonsContainer = new FillFlowContainer
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
title = new OsuSpriteText
|
||||||
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
|
{
|
||||||
Spacing = new Vector2(buttons_spacing),
|
Font = OsuFont.GetFont(size: 37, weight: FontWeight.Bold, italics: true)
|
||||||
|
},
|
||||||
|
externalLink = new ExternalLinkButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Margin = new MarginPadding { Left = 3, Bottom = 4 }, //To better lineup with the font
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 25, weight: FontWeight.SemiBold, italics: true) },
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Margin = new MarginPadding { Top = 20 },
|
||||||
|
Child = author = new AuthorInfo(),
|
||||||
|
},
|
||||||
|
beatmapAvailability = new BeatmapAvailability(),
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = buttons_height,
|
||||||
|
Margin = new MarginPadding { Top = 10 },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
favouriteButton = new FavouriteButton(),
|
||||||
|
downloadButtonsContainer = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
|
||||||
|
Spacing = new Vector2(buttons_spacing),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
|
},
|
||||||
|
loading = new LoadingAnimation
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(1.5f),
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
@ -187,8 +205,11 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Picker.Beatmap.ValueChanged += b => Details.Beatmap = b.NewValue;
|
Picker.Beatmap.ValueChanged += b =>
|
||||||
Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}";
|
{
|
||||||
|
Details.Beatmap = b.NewValue;
|
||||||
|
externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}";
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -200,25 +221,36 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
|
|
||||||
BeatmapSet.BindValueChanged(setInfo =>
|
BeatmapSet.BindValueChanged(setInfo =>
|
||||||
{
|
{
|
||||||
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = setInfo.NewValue;
|
Picker.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue;
|
||||||
|
|
||||||
title.Text = setInfo.NewValue?.Metadata.Title ?? string.Empty;
|
|
||||||
artist.Text = setInfo.NewValue?.Metadata.Artist ?? string.Empty;
|
|
||||||
onlineStatusPill.Status = setInfo.NewValue?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
|
|
||||||
cover.BeatmapSet = setInfo.NewValue;
|
cover.BeatmapSet = setInfo.NewValue;
|
||||||
|
|
||||||
if (setInfo.NewValue != null)
|
if (setInfo.NewValue == null)
|
||||||
{
|
|
||||||
downloadButtonsContainer.FadeIn(transition_duration);
|
|
||||||
favouriteButton.FadeIn(transition_duration);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
|
onlineStatusPill.FadeTo(0.5f, 500, Easing.OutQuint);
|
||||||
|
fadeContent.Hide();
|
||||||
|
|
||||||
|
loading.Show();
|
||||||
|
|
||||||
downloadButtonsContainer.FadeOut(transition_duration);
|
downloadButtonsContainer.FadeOut(transition_duration);
|
||||||
favouriteButton.FadeOut(transition_duration);
|
favouriteButton.FadeOut(transition_duration);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fadeContent.FadeIn(500, Easing.OutQuint);
|
||||||
|
|
||||||
updateDownloadButtons();
|
loading.Hide();
|
||||||
|
|
||||||
|
title.Text = setInfo.NewValue.Metadata.Title ?? string.Empty;
|
||||||
|
artist.Text = setInfo.NewValue.Metadata.Artist ?? string.Empty;
|
||||||
|
|
||||||
|
onlineStatusPill.FadeIn(500, Easing.OutQuint);
|
||||||
|
onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status;
|
||||||
|
|
||||||
|
downloadButtonsContainer.FadeIn(transition_duration);
|
||||||
|
favouriteButton.FadeIn(transition_duration);
|
||||||
|
|
||||||
|
updateDownloadButtons();
|
||||||
|
}
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,11 +258,17 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
{
|
{
|
||||||
if (BeatmapSet.Value == null) return;
|
if (BeatmapSet.Value == null) return;
|
||||||
|
|
||||||
|
if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false)
|
||||||
|
{
|
||||||
|
downloadButtonsContainer.Clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (State.Value)
|
switch (State.Value)
|
||||||
{
|
{
|
||||||
case DownloadState.LocallyAvailable:
|
case DownloadState.LocallyAvailable:
|
||||||
// temporary for UX until new design is implemented.
|
// temporary for UX until new design is implemented.
|
||||||
downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet.Value)
|
downloadButtonsContainer.Child = new Direct.DownloadButton(BeatmapSet.Value)
|
||||||
{
|
{
|
||||||
Width = 50,
|
Width = 50,
|
||||||
RelativeSizeAxes = Axes.Y
|
RelativeSizeAxes = Axes.Y
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
Size = new Vector2(40),
|
Size = new Vector2(40),
|
||||||
FillMode = FillMode.Fit,
|
FillMode = FillMode.Fit,
|
||||||
},
|
},
|
||||||
avatar = new UpdateableAvatar
|
avatar = new UpdateableAvatar(hideImmediately: true)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -90,7 +90,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold)
|
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold)
|
||||||
},
|
},
|
||||||
flag = new UpdateableFlag
|
flag = new UpdateableFlag(hideImmediately: true)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
|
@ -27,12 +27,10 @@ namespace osu.Game.Overlays
|
|||||||
public const float TOP_PADDING = 25;
|
public const float TOP_PADDING = 25;
|
||||||
public const float RIGHT_WIDTH = 275;
|
public const float RIGHT_WIDTH = 275;
|
||||||
|
|
||||||
private readonly Header header;
|
protected readonly Header Header;
|
||||||
|
|
||||||
private RulesetStore rulesets;
|
private RulesetStore rulesets;
|
||||||
|
|
||||||
private readonly OsuScrollContainer scroll;
|
|
||||||
|
|
||||||
private readonly Bindable<BeatmapSetInfo> beatmapSet = new Bindable<BeatmapSetInfo>();
|
private readonly Bindable<BeatmapSetInfo> beatmapSet = new Bindable<BeatmapSetInfo>();
|
||||||
|
|
||||||
// receive input outside our bounds so we can trigger a close event on ourselves.
|
// receive input outside our bounds so we can trigger a close event on ourselves.
|
||||||
@ -40,6 +38,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
public BeatmapSetOverlay()
|
public BeatmapSetOverlay()
|
||||||
{
|
{
|
||||||
|
OsuScrollContainer scroll;
|
||||||
Info info;
|
Info info;
|
||||||
ScoresContainer scores;
|
ScoresContainer scores;
|
||||||
|
|
||||||
@ -61,7 +60,7 @@ namespace osu.Game.Overlays
|
|||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
header = new Header(),
|
Header = new Header(),
|
||||||
info = new Info(),
|
info = new Info(),
|
||||||
scores = new ScoresContainer(),
|
scores = new ScoresContainer(),
|
||||||
},
|
},
|
||||||
@ -69,13 +68,15 @@ namespace osu.Game.Overlays
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
header.BeatmapSet.BindTo(beatmapSet);
|
Header.BeatmapSet.BindTo(beatmapSet);
|
||||||
info.BeatmapSet.BindTo(beatmapSet);
|
info.BeatmapSet.BindTo(beatmapSet);
|
||||||
|
|
||||||
header.Picker.Beatmap.ValueChanged += b =>
|
Header.Picker.Beatmap.ValueChanged += b =>
|
||||||
{
|
{
|
||||||
info.Beatmap = b.NewValue;
|
info.Beatmap = b.NewValue;
|
||||||
scores.Beatmap = b.NewValue;
|
scores.Beatmap = b.NewValue;
|
||||||
|
|
||||||
|
scroll.ScrollToStart();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ namespace osu.Game.Overlays
|
|||||||
req.Success += res =>
|
req.Success += res =>
|
||||||
{
|
{
|
||||||
beatmapSet.Value = res.ToBeatmapSet(rulesets);
|
beatmapSet.Value = res.ToBeatmapSet(rulesets);
|
||||||
header.Picker.Beatmap.Value = header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
|
Header.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId);
|
||||||
};
|
};
|
||||||
API.Queue(req);
|
API.Queue(req);
|
||||||
Show();
|
Show();
|
||||||
@ -119,11 +120,14 @@ namespace osu.Game.Overlays
|
|||||||
Show();
|
Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show an already fully-populated beatmap set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="set">The set to show.</param>
|
||||||
public void ShowBeatmapSet(BeatmapSetInfo set)
|
public void ShowBeatmapSet(BeatmapSetInfo set)
|
||||||
{
|
{
|
||||||
beatmapSet.Value = set;
|
beatmapSet.Value = set;
|
||||||
Show();
|
Show();
|
||||||
scroll.ScrollTo(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,9 +134,9 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Position = new Vector2(0f, -50f),
|
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Spacing = new Vector2(0f, 10f),
|
Spacing = new Vector2(0f, 10f),
|
||||||
|
Padding = new MarginPadding { Bottom = 10 },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
@ -144,10 +144,6 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Size = ringSize,
|
Size = ringSize,
|
||||||
Margin = new MarginPadding
|
|
||||||
{
|
|
||||||
Bottom = 30,
|
|
||||||
},
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
ring = new CircularContainer
|
ring = new CircularContainer
|
||||||
@ -181,15 +177,15 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding(15),
|
|
||||||
TextAnchor = Anchor.TopCentre,
|
TextAnchor = Anchor.TopCentre,
|
||||||
},
|
},
|
||||||
body = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 18))
|
body = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 18))
|
||||||
{
|
{
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
TextAnchor = Anchor.TopCentre,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding(15),
|
|
||||||
TextAnchor = Anchor.TopCentre,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
19
osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs
Normal file
19
osu.Game/Overlays/Direct/BeatmapDownloadTrackingComposite.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Online;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Direct
|
||||||
|
{
|
||||||
|
public abstract class BeatmapDownloadTrackingComposite : DownloadTrackingComposite<BeatmapSetInfo, BeatmapManager>
|
||||||
|
{
|
||||||
|
public Bindable<BeatmapSetInfo> BeatmapSet => Model;
|
||||||
|
|
||||||
|
protected BeatmapDownloadTrackingComposite(BeatmapSetInfo set = null)
|
||||||
|
: base(set)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ namespace osu.Game.Overlays.Direct
|
|||||||
private const float height = 70;
|
private const float height = 70;
|
||||||
|
|
||||||
private FillFlowContainer statusContainer;
|
private FillFlowContainer statusContainer;
|
||||||
|
protected DownloadButton DownloadButton;
|
||||||
private PlayButton playButton;
|
private PlayButton playButton;
|
||||||
private Box progressBar;
|
private Box progressBar;
|
||||||
|
|
||||||
@ -149,7 +150,7 @@ namespace osu.Game.Overlays.Direct
|
|||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Child = new DownloadButton(SetInfo)
|
Child = DownloadButton = new DownloadButton(SetInfo)
|
||||||
{
|
{
|
||||||
Size = new Vector2(height - vertical_padding * 3),
|
Size = new Vector2(height - vertical_padding * 3),
|
||||||
Margin = new MarginPadding { Left = vertical_padding * 2, Right = vertical_padding },
|
Margin = new MarginPadding { Left = vertical_padding * 2, Right = vertical_padding },
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// 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 System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -34,6 +35,7 @@ namespace osu.Game.Overlays.Direct
|
|||||||
|
|
||||||
public PreviewTrack Preview => PlayButton.Preview;
|
public PreviewTrack Preview => PlayButton.Preview;
|
||||||
public Bindable<bool> PreviewPlaying => PlayButton.Playing;
|
public Bindable<bool> PreviewPlaying => PlayButton.Playing;
|
||||||
|
|
||||||
protected abstract PlayButton PlayButton { get; }
|
protected abstract PlayButton PlayButton { get; }
|
||||||
protected abstract Box PreviewBar { get; }
|
protected abstract Box PreviewBar { get; }
|
||||||
|
|
||||||
@ -43,6 +45,8 @@ namespace osu.Game.Overlays.Direct
|
|||||||
|
|
||||||
protected DirectPanel(BeatmapSetInfo setInfo)
|
protected DirectPanel(BeatmapSetInfo setInfo)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(setInfo.OnlineBeatmapSetID != null);
|
||||||
|
|
||||||
SetInfo = setInfo;
|
SetInfo = setInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,12 +121,11 @@ namespace osu.Game.Overlays.Direct
|
|||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
ShowInformation();
|
Debug.Assert(SetInfo.OnlineBeatmapSetID != null);
|
||||||
|
beatmapSetOverlay?.FetchAndShowBeatmapSet(SetInfo.OnlineBeatmapSetID.Value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo);
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
@ -9,21 +9,22 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Overlays.Direct
|
||||||
{
|
{
|
||||||
public class DownloadButton : DownloadTrackingComposite
|
public class DownloadButton : BeatmapDownloadTrackingComposite
|
||||||
{
|
{
|
||||||
|
protected bool DownloadEnabled => button.Enabled.Value;
|
||||||
|
|
||||||
private readonly bool noVideo;
|
private readonly bool noVideo;
|
||||||
private readonly SpriteIcon icon;
|
private readonly SpriteIcon icon;
|
||||||
private readonly SpriteIcon checkmark;
|
private readonly SpriteIcon checkmark;
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
|
|
||||||
private OsuColour colours;
|
private OsuColour colours;
|
||||||
|
|
||||||
private readonly ShakeContainer shakeContainer;
|
private readonly ShakeContainer shakeContainer;
|
||||||
|
|
||||||
private readonly OsuAnimatedButton button;
|
private readonly OsuAnimatedButton button;
|
||||||
|
|
||||||
public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
|
public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
|
||||||
@ -72,11 +73,18 @@ namespace osu.Game.Overlays.Direct
|
|||||||
FinishTransforms(true);
|
FinishTransforms(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(permitNulls: true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(OsuColour colours, OsuGame game, BeatmapManager beatmaps)
|
private void load(OsuColour colours, OsuGame game, BeatmapManager beatmaps)
|
||||||
{
|
{
|
||||||
this.colours = colours;
|
this.colours = colours;
|
||||||
|
|
||||||
|
if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false)
|
||||||
|
{
|
||||||
|
button.Enabled.Value = false;
|
||||||
|
button.TooltipText = "This beatmap is currently not available for download.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
button.Action = () =>
|
button.Action = () =>
|
||||||
{
|
{
|
||||||
switch (State.Value)
|
switch (State.Value)
|
||||||
|
@ -7,11 +7,12 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Overlays.Direct
|
||||||
{
|
{
|
||||||
public class DownloadProgressBar : DownloadTrackingComposite
|
public class DownloadProgressBar : BeatmapDownloadTrackingComposite
|
||||||
{
|
{
|
||||||
private readonly ProgressBar progressBar;
|
private readonly ProgressBar progressBar;
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Music
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(BeatmapManager beatmaps, IBindable<WorkingBeatmap> beatmap)
|
private void load(BeatmapManager beatmaps, IBindable<WorkingBeatmap> beatmap)
|
||||||
{
|
{
|
||||||
beatmaps.GetAllUsableBeatmapSets().ForEach(b => addBeatmapSet(b, false));
|
beatmaps.GetAllUsableBeatmapSets().ForEach(addBeatmapSet);
|
||||||
beatmaps.ItemAdded += addBeatmapSet;
|
beatmaps.ItemAdded += addBeatmapSet;
|
||||||
beatmaps.ItemRemoved += removeBeatmapSet;
|
beatmaps.ItemRemoved += removeBeatmapSet;
|
||||||
|
|
||||||
@ -83,11 +83,8 @@ namespace osu.Game.Overlays.Music
|
|||||||
beatmapBacking.ValueChanged += _ => updateSelectedSet();
|
beatmapBacking.ValueChanged += _ => updateSelectedSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addBeatmapSet(BeatmapSetInfo obj, bool existing) => Schedule(() =>
|
private void addBeatmapSet(BeatmapSetInfo obj) => Schedule(() =>
|
||||||
{
|
{
|
||||||
if (existing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var newItem = new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) };
|
var newItem = new PlaylistItem(obj) { OnSelect = set => Selected?.Invoke(set) };
|
||||||
|
|
||||||
items.Add(newItem);
|
items.Add(newItem);
|
||||||
|
@ -218,15 +218,9 @@ namespace osu.Game.Overlays
|
|||||||
beatmapSets.Insert(index, beatmapSetInfo);
|
beatmapSets.Insert(index, beatmapSetInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBeatmapAdded(BeatmapSetInfo obj, bool existing)
|
private void handleBeatmapAdded(BeatmapSetInfo set) => Schedule(() => beatmapSets.Add(set));
|
||||||
{
|
|
||||||
if (existing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Schedule(() => beatmapSets.Add(obj));
|
private void handleBeatmapRemoved(BeatmapSetInfo set) => Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID));
|
||||||
}
|
|
||||||
|
|
||||||
private void handleBeatmapRemoved(BeatmapSetInfo obj) => Schedule(() => beatmapSets.RemoveAll(s => s.ID == obj.ID));
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
@ -259,6 +253,12 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
|
if (pendingBeatmapSwitch != null)
|
||||||
|
{
|
||||||
|
pendingBeatmapSwitch();
|
||||||
|
pendingBeatmapSwitch = null;
|
||||||
|
}
|
||||||
|
|
||||||
var track = current?.TrackLoaded ?? false ? current.Track : null;
|
var track = current?.TrackLoaded ?? false ? current.Track : null;
|
||||||
|
|
||||||
if (track?.IsDummyDevice == false)
|
if (track?.IsDummyDevice == false)
|
||||||
@ -368,15 +368,12 @@ namespace osu.Game.Overlays
|
|||||||
mod.ApplyToClock(track);
|
mod.ApplyToClock(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate pendingBeatmapSwitch;
|
private Action pendingBeatmapSwitch;
|
||||||
|
|
||||||
private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction)
|
private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction)
|
||||||
{
|
{
|
||||||
//we might be off-screen when this update comes in.
|
// avoid using scheduler as our scheduler may not be run for a long time, holding references to beatmaps.
|
||||||
//rather than Scheduling, manually handle this to avoid possible memory contention.
|
pendingBeatmapSwitch = delegate
|
||||||
pendingBeatmapSwitch?.Cancel();
|
|
||||||
|
|
||||||
pendingBeatmapSwitch = Schedule(delegate
|
|
||||||
{
|
{
|
||||||
// todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
|
// todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
@ -416,7 +413,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
playerContainer.Add(newBackground);
|
playerContainer.Add(newBackground);
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
|
@ -1,153 +1,23 @@
|
|||||||
// 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.Shapes;
|
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
public class OverlayHeaderTabControl : TabControl<string>
|
public class OverlayHeaderTabControl : OverlayTabControl<string>
|
||||||
{
|
{
|
||||||
private readonly Box bar;
|
protected override TabItem<string> CreateTabItem(string value) => new OverlayHeaderTabItem(value)
|
||||||
|
|
||||||
private Color4 accentColour = Color4.White;
|
|
||||||
|
|
||||||
public Color4 AccentColour
|
|
||||||
{
|
{
|
||||||
get => accentColour;
|
AccentColour = AccentColour,
|
||||||
set
|
|
||||||
{
|
|
||||||
if (accentColour == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
accentColour = value;
|
|
||||||
bar.Colour = value;
|
|
||||||
|
|
||||||
foreach (TabItem<string> tabItem in TabContainer)
|
|
||||||
{
|
|
||||||
((HeaderTabItem)tabItem).AccentColour = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public new MarginPadding Padding
|
|
||||||
{
|
|
||||||
get => TabContainer.Padding;
|
|
||||||
set => TabContainer.Padding = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OverlayHeaderTabControl()
|
|
||||||
{
|
|
||||||
TabContainer.Masking = false;
|
|
||||||
TabContainer.Spacing = new Vector2(15, 0);
|
|
||||||
|
|
||||||
AddInternal(bar = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = 2,
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.CentreLeft
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Dropdown<string> CreateDropdown() => null;
|
|
||||||
|
|
||||||
protected override TabItem<string> CreateTabItem(string value) => new HeaderTabItem(value)
|
|
||||||
{
|
|
||||||
AccentColour = AccentColour
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private class HeaderTabItem : TabItem<string>
|
private class OverlayHeaderTabItem : OverlayTabItem<string>
|
||||||
{
|
{
|
||||||
private readonly OsuSpriteText text;
|
public OverlayHeaderTabItem(string value)
|
||||||
private readonly ExpandingBar bar;
|
|
||||||
|
|
||||||
private Color4 accentColour;
|
|
||||||
|
|
||||||
public Color4 AccentColour
|
|
||||||
{
|
|
||||||
get => accentColour;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (accentColour == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
accentColour = value;
|
|
||||||
bar.Colour = value;
|
|
||||||
|
|
||||||
updateState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public HeaderTabItem(string value)
|
|
||||||
: base(value)
|
: base(value)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.X;
|
Text.Text = value;
|
||||||
RelativeSizeAxes = Axes.Y;
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
text = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Margin = new MarginPadding { Bottom = 10 },
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Text = value,
|
|
||||||
Font = OsuFont.GetFont()
|
|
||||||
},
|
|
||||||
bar = new ExpandingBar
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
ExpandedSize = 7.5f,
|
|
||||||
CollapsedSize = 0
|
|
||||||
},
|
|
||||||
new HoverClickSounds()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
|
||||||
{
|
|
||||||
base.OnHover(e);
|
|
||||||
|
|
||||||
updateState();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnHoverLost(HoverLostEvent e)
|
|
||||||
{
|
|
||||||
base.OnHoverLost(e);
|
|
||||||
|
|
||||||
updateState();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnActivated() => updateState();
|
|
||||||
|
|
||||||
protected override void OnDeactivated() => updateState();
|
|
||||||
|
|
||||||
private void updateState()
|
|
||||||
{
|
|
||||||
if (Active.Value || IsHovered)
|
|
||||||
{
|
|
||||||
text.FadeColour(Color4.White, 120, Easing.InQuad);
|
|
||||||
bar.Expand();
|
|
||||||
|
|
||||||
if (Active.Value)
|
|
||||||
text.Font = text.Font.With(weight: FontWeight.Bold);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
text.FadeColour(AccentColour, 120, Easing.InQuad);
|
|
||||||
bar.Collapse();
|
|
||||||
text.Font = text.Font.With(weight: FontWeight.Medium);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
163
osu.Game/Overlays/OverlayTabControl.cs
Normal file
163
osu.Game/Overlays/OverlayTabControl.cs
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays
|
||||||
|
{
|
||||||
|
public abstract class OverlayTabControl<T> : TabControl<T>
|
||||||
|
{
|
||||||
|
private readonly Box bar;
|
||||||
|
|
||||||
|
private Color4 accentColour = Color4.White;
|
||||||
|
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get => accentColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (accentColour == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
accentColour = value;
|
||||||
|
bar.Colour = value;
|
||||||
|
|
||||||
|
foreach (TabItem<T> tabItem in TabContainer)
|
||||||
|
{
|
||||||
|
((OverlayTabItem<T>)tabItem).AccentColour = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public new MarginPadding Padding
|
||||||
|
{
|
||||||
|
get => TabContainer.Padding;
|
||||||
|
set => TabContainer.Padding = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OverlayTabControl()
|
||||||
|
{
|
||||||
|
TabContainer.Masking = false;
|
||||||
|
TabContainer.Spacing = new Vector2(15, 0);
|
||||||
|
|
||||||
|
AddInternal(bar = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 2,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.CentreLeft
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Dropdown<T> CreateDropdown() => null;
|
||||||
|
|
||||||
|
protected override TabItem<T> CreateTabItem(T value) => new OverlayTabItem<T>(value);
|
||||||
|
|
||||||
|
protected class OverlayTabItem<U> : TabItem<U>
|
||||||
|
{
|
||||||
|
private readonly ExpandingBar bar;
|
||||||
|
|
||||||
|
protected readonly OsuSpriteText Text;
|
||||||
|
|
||||||
|
private Color4 accentColour;
|
||||||
|
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get => accentColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (accentColour == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
accentColour = value;
|
||||||
|
bar.Colour = value;
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public OverlayTabItem(U value)
|
||||||
|
: base(value)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X;
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
Text = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Margin = new MarginPadding { Bottom = 10 },
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Font = OsuFont.GetFont(),
|
||||||
|
},
|
||||||
|
bar = new ExpandingBar
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
ExpandedSize = 7.5f,
|
||||||
|
CollapsedSize = 0
|
||||||
|
},
|
||||||
|
new HoverClickSounds()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
base.OnHover(e);
|
||||||
|
|
||||||
|
if (!Active.Value)
|
||||||
|
HoverAction();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
|
||||||
|
if (!Active.Value)
|
||||||
|
UnhoverAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnActivated()
|
||||||
|
{
|
||||||
|
HoverAction();
|
||||||
|
Text.Font = Text.Font.With(weight: FontWeight.Bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDeactivated()
|
||||||
|
{
|
||||||
|
UnhoverAction();
|
||||||
|
Text.Font = Text.Font.With(weight: FontWeight.Medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
|
{
|
||||||
|
if (Active.Value)
|
||||||
|
OnActivated();
|
||||||
|
else
|
||||||
|
OnDeactivated();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void HoverAction()
|
||||||
|
{
|
||||||
|
bar.Expand();
|
||||||
|
Text.FadeColour(Color4.White, 120, Easing.InQuad);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UnhoverAction()
|
||||||
|
{
|
||||||
|
bar.Collapse();
|
||||||
|
Text.FadeColour(AccentColour, 120, Easing.InQuad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
|
{
|
||||||
|
public class ProfileRulesetSelector : RulesetSelector
|
||||||
|
{
|
||||||
|
private Color4 accentColour = Color4.White;
|
||||||
|
|
||||||
|
public ProfileRulesetSelector()
|
||||||
|
{
|
||||||
|
TabContainer.Masking = false;
|
||||||
|
TabContainer.Spacing = new Vector2(10, 0);
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
accentColour = colours.Seafoam;
|
||||||
|
|
||||||
|
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
|
||||||
|
((ProfileRulesetTabItem)tabItem).AccentColour = accentColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDefaultRuleset(RulesetInfo ruleset)
|
||||||
|
{
|
||||||
|
// Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value
|
||||||
|
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
|
||||||
|
((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SelectDefaultRuleset()
|
||||||
|
{
|
||||||
|
// Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value
|
||||||
|
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
|
||||||
|
{
|
||||||
|
if (((ProfileRulesetTabItem)tabItem).IsDefault)
|
||||||
|
{
|
||||||
|
Current.Value = ((ProfileRulesetTabItem)tabItem).Value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value)
|
||||||
|
{
|
||||||
|
AccentColour = accentColour
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
|
||||||
|
{
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
|
{
|
||||||
|
public class ProfileRulesetTabItem : TabItem<RulesetInfo>, IHasAccentColour
|
||||||
|
{
|
||||||
|
private readonly OsuSpriteText text;
|
||||||
|
private readonly SpriteIcon icon;
|
||||||
|
|
||||||
|
private Color4 accentColour;
|
||||||
|
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get => accentColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (accentColour == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
accentColour = value;
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isDefault;
|
||||||
|
|
||||||
|
public bool IsDefault
|
||||||
|
{
|
||||||
|
get => isDefault;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (isDefault == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
isDefault = value;
|
||||||
|
|
||||||
|
icon.FadeTo(isDefault ? 1 : 0, 200, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProfileRulesetTabItem(RulesetInfo value)
|
||||||
|
: base(value)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(3, 0),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
text = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Text = value.Name,
|
||||||
|
},
|
||||||
|
icon = new SpriteIcon
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Alpha = 0,
|
||||||
|
AlwaysPresent = true,
|
||||||
|
Icon = FontAwesome.Solid.Star,
|
||||||
|
Size = new Vector2(12),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new HoverClickSounds()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
base.OnHover(e);
|
||||||
|
updateState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnActivated() => updateState();
|
||||||
|
|
||||||
|
protected override void OnDeactivated() => updateState();
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
|
{
|
||||||
|
text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium);
|
||||||
|
|
||||||
|
if (IsHovered || Active.Value)
|
||||||
|
{
|
||||||
|
text.FadeColour(Color4.White, 120, Easing.InQuad);
|
||||||
|
icon.FadeColour(Color4.White, 120, Easing.InQuad);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text.FadeColour(AccentColour, 120, Easing.InQuad);
|
||||||
|
icon.FadeColour(AccentColour, 120, Easing.InQuad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,29 @@
|
|||||||
// 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.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Backgrounds;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Profile
|
namespace osu.Game.Overlays.Profile
|
||||||
{
|
{
|
||||||
public abstract class ProfileSection : FillFlowContainer
|
public abstract class ProfileSection : Container
|
||||||
{
|
{
|
||||||
public abstract string Title { get; }
|
public abstract string Title { get; }
|
||||||
|
|
||||||
public abstract string Identifier { get; }
|
public abstract string Identifier { get; }
|
||||||
|
|
||||||
private readonly FillFlowContainer content;
|
private readonly FillFlowContainer content;
|
||||||
|
private readonly Box background;
|
||||||
|
private readonly Box underscore;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
@ -27,50 +31,109 @@ namespace osu.Game.Overlays.Profile
|
|||||||
|
|
||||||
protected ProfileSection()
|
protected ProfileSection()
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Vertical;
|
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuSpriteText
|
background = new Box
|
||||||
{
|
{
|
||||||
Text = Title,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular, italics: true),
|
|
||||||
Margin = new MarginPadding
|
|
||||||
{
|
|
||||||
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
|
|
||||||
Vertical = 10
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
content = new FillFlowContainer
|
new SectionTriangles
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Padding = new MarginPadding
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
|
new Container
|
||||||
Bottom = 20
|
{
|
||||||
}
|
AutoSizeAxes = Axes.Both,
|
||||||
},
|
Margin = new MarginPadding
|
||||||
new Box
|
{
|
||||||
{
|
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
|
||||||
RelativeSizeAxes = Axes.X,
|
Top = 15,
|
||||||
Height = 1,
|
Bottom = 10,
|
||||||
Colour = OsuColour.Gray(34),
|
},
|
||||||
EdgeSmoothness = new Vector2(1)
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = Title,
|
||||||
|
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold),
|
||||||
|
},
|
||||||
|
underscore = new Box
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Margin = new MarginPadding { Top = 4 },
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
|
||||||
|
Bottom = 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// placeholder
|
[BackgroundDependencyLoader]
|
||||||
Add(new OsuSpriteText
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
background.Colour = colours.GreySeafoamDarker;
|
||||||
|
underscore.Colour = colours.Seafoam;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SectionTriangles : Container
|
||||||
|
{
|
||||||
|
private readonly Triangles triangles;
|
||||||
|
private readonly Box foreground;
|
||||||
|
|
||||||
|
public SectionTriangles()
|
||||||
{
|
{
|
||||||
Text = @"coming soon!",
|
RelativeSizeAxes = Axes.X;
|
||||||
Colour = Color4.Gray,
|
Height = 100;
|
||||||
Anchor = Anchor.Centre,
|
Masking = true;
|
||||||
Origin = Anchor.Centre,
|
Children = new Drawable[]
|
||||||
Margin = new MarginPadding { Top = 100, Bottom = 100 }
|
{
|
||||||
});
|
triangles = new Triangles
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
TriangleScale = 3,
|
||||||
|
},
|
||||||
|
foreground = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
triangles.ColourLight = colours.GreySeafoamDark;
|
||||||
|
triangles.ColourDark = colours.GreySeafoamDarker.Darken(0.2f);
|
||||||
|
foreground.Colour = ColourInfo.GradientVertical(colours.GreySeafoamDarker, colours.GreySeafoamDarker.Opacity(0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
|||||||
|
|
||||||
private class AudioDeviceSettingsDropdown : SettingsDropdown<string>
|
private class AudioDeviceSettingsDropdown : SettingsDropdown<string>
|
||||||
{
|
{
|
||||||
protected override OsuDropdown<string> CreateDropdown() => new AudioDeviceDropdownControl { Items = Items };
|
protected override OsuDropdown<string> CreateDropdown() => new AudioDeviceDropdownControl();
|
||||||
|
|
||||||
private class AudioDeviceDropdownControl : DropdownControl
|
private class AudioDeviceDropdownControl : DropdownControl
|
||||||
{
|
{
|
||||||
|
@ -237,7 +237,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
|
|
||||||
private class ResolutionSettingsDropdown : SettingsDropdown<Size>
|
private class ResolutionSettingsDropdown : SettingsDropdown<Size>
|
||||||
{
|
{
|
||||||
protected override OsuDropdown<Size> CreateDropdown() => new ResolutionDropdownControl { Items = Items };
|
protected override OsuDropdown<Size> CreateDropdown() => new ResolutionDropdownControl();
|
||||||
|
|
||||||
private class ResolutionDropdownControl : DropdownControl
|
private class ResolutionDropdownControl : DropdownControl
|
||||||
{
|
{
|
||||||
|
@ -82,13 +82,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
|
|
||||||
private void itemRemoved(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != s.ID).ToArray());
|
private void itemRemoved(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != s.ID).ToArray());
|
||||||
|
|
||||||
private void itemAdded(SkinInfo s, bool existing)
|
private void itemAdded(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Append(s).ToArray());
|
||||||
{
|
|
||||||
if (existing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Schedule(() => skinDropdown.Items = skinDropdown.Items.Append(s).ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
@ -108,7 +102,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
|
|
||||||
private class SkinSettingsDropdown : SettingsDropdown<SkinInfo>
|
private class SkinSettingsDropdown : SettingsDropdown<SkinInfo>
|
||||||
{
|
{
|
||||||
protected override OsuDropdown<SkinInfo> CreateDropdown() => new SkinDropdownControl { Items = Items };
|
protected override OsuDropdown<SkinInfo> CreateDropdown() => new SkinDropdownControl();
|
||||||
|
|
||||||
private class SkinDropdownControl : DropdownControl
|
private class SkinDropdownControl : DropdownControl
|
||||||
{
|
{
|
||||||
|
@ -13,39 +13,23 @@ namespace osu.Game.Overlays.Settings
|
|||||||
{
|
{
|
||||||
protected new OsuDropdown<T> Control => (OsuDropdown<T>)base.Control;
|
protected new OsuDropdown<T> Control => (OsuDropdown<T>)base.Control;
|
||||||
|
|
||||||
private IEnumerable<T> items = Enumerable.Empty<T>();
|
|
||||||
|
|
||||||
public IEnumerable<T> Items
|
public IEnumerable<T> Items
|
||||||
{
|
{
|
||||||
get => items;
|
get => Control.Items;
|
||||||
set
|
set => Control.Items = value;
|
||||||
{
|
|
||||||
items = value;
|
|
||||||
|
|
||||||
if (Control != null)
|
|
||||||
Control.Items = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBindableList<T> itemSource;
|
|
||||||
|
|
||||||
public IBindableList<T> ItemSource
|
public IBindableList<T> ItemSource
|
||||||
{
|
{
|
||||||
get => itemSource;
|
get => Control.ItemSource;
|
||||||
set
|
set => Control.ItemSource = value;
|
||||||
{
|
|
||||||
itemSource = value;
|
|
||||||
|
|
||||||
if (Control != null)
|
|
||||||
Control.ItemSource = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> FilterTerms => base.FilterTerms.Concat(Control.Items.Select(i => i.ToString()));
|
public override IEnumerable<string> FilterTerms => base.FilterTerms.Concat(Control.Items.Select(i => i.ToString()));
|
||||||
|
|
||||||
protected sealed override Drawable CreateControl() => CreateDropdown();
|
protected sealed override Drawable CreateControl() => CreateDropdown();
|
||||||
|
|
||||||
protected virtual OsuDropdown<T> CreateDropdown() => new DropdownControl { Items = Items, ItemSource = ItemSource };
|
protected virtual OsuDropdown<T> CreateDropdown() => new DropdownControl();
|
||||||
|
|
||||||
protected class DropdownControl : OsuDropdown<T>
|
protected class DropdownControl : OsuDropdown<T>
|
||||||
{
|
{
|
||||||
|
17
osu.Game/Overlays/Settings/SettingsNumberBox.cs
Normal file
17
osu.Game/Overlays/Settings/SettingsNumberBox.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// 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.Graphics.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Settings
|
||||||
|
{
|
||||||
|
public class SettingsNumberBox : SettingsItem<string>
|
||||||
|
{
|
||||||
|
protected override Drawable CreateControl() => new OsuNumberBox
|
||||||
|
{
|
||||||
|
Margin = new MarginPadding { Top = 5 },
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -34,6 +34,8 @@ namespace osu.Game.Overlays
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool DimMainContent => false; // dimming is handled by main overlay
|
||||||
|
|
||||||
private class BackButton : OsuClickableContainer, IKeyBindingHandler<GlobalAction>
|
private class BackButton : OsuClickableContainer, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
private AspectContainer aspect;
|
private AspectContainer aspect;
|
||||||
|
@ -12,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Toolbar
|
namespace osu.Game.Overlays.Toolbar
|
||||||
{
|
{
|
||||||
@ -23,6 +24,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
public Action OnHome;
|
public Action OnHome;
|
||||||
|
|
||||||
private ToolbarUserButton userButton;
|
private ToolbarUserButton userButton;
|
||||||
|
private ToolbarRulesetSelector rulesetSelector;
|
||||||
|
|
||||||
protected override bool BlockPositionalInput => false;
|
protected override bool BlockPositionalInput => false;
|
||||||
|
|
||||||
@ -40,7 +42,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(OsuGame osuGame)
|
private void load(OsuGame osuGame, Bindable<RulesetInfo> parentRuleset)
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -57,7 +59,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
{
|
{
|
||||||
Action = () => OnHome?.Invoke()
|
Action = () => OnHome?.Invoke()
|
||||||
},
|
},
|
||||||
new ToolbarRulesetSelector()
|
rulesetSelector = new ToolbarRulesetSelector()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
@ -84,6 +86,9 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Bound after the selector is added to the hierarchy to give it a chance to load the available rulesets
|
||||||
|
rulesetSelector.Current.BindTo(parentRuleset);
|
||||||
|
|
||||||
State.ValueChanged += visibility =>
|
State.ValueChanged += visibility =>
|
||||||
{
|
{
|
||||||
if (overlayActivationMode.Value == OverlayActivation.Disabled)
|
if (overlayActivationMode.Value == OverlayActivation.Disabled)
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using osu.Framework.Graphics.Effects;
|
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Toolbar
|
|
||||||
{
|
|
||||||
public class ToolbarRulesetButton : ToolbarButton
|
|
||||||
{
|
|
||||||
private RulesetInfo ruleset;
|
|
||||||
|
|
||||||
public RulesetInfo Ruleset
|
|
||||||
{
|
|
||||||
get => ruleset;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
ruleset = value;
|
|
||||||
|
|
||||||
var rInstance = ruleset.CreateInstance();
|
|
||||||
|
|
||||||
TooltipMain = rInstance.Description;
|
|
||||||
TooltipSub = $"Play some {rInstance.Description}";
|
|
||||||
SetIcon(rInstance.CreateIcon());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Active
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
IconContainer.Colour = Color4.White;
|
|
||||||
IconContainer.EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Glow,
|
|
||||||
Colour = new Color4(255, 194, 224, 100),
|
|
||||||
Radius = 15,
|
|
||||||
Roundness = 15,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IconContainer.Colour = new Color4(255, 194, 224, 255);
|
|
||||||
IconContainer.EdgeEffect = new EdgeEffectParameters();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
IconContainer.Scale *= 1.4f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +1,41 @@
|
|||||||
// 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.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Caching;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osuTK.Input;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Toolbar
|
namespace osu.Game.Overlays.Toolbar
|
||||||
{
|
{
|
||||||
public class ToolbarRulesetSelector : Container
|
public class ToolbarRulesetSelector : RulesetSelector
|
||||||
{
|
{
|
||||||
private const float padding = 10;
|
private const float padding = 10;
|
||||||
|
|
||||||
private readonly FillFlowContainer modeButtons;
|
private Drawable modeButtonLine;
|
||||||
private readonly Drawable modeButtonLine;
|
|
||||||
private ToolbarRulesetButton activeButton;
|
|
||||||
|
|
||||||
private RulesetStore rulesets;
|
|
||||||
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
|
||||||
|
|
||||||
public ToolbarRulesetSelector()
|
public ToolbarRulesetSelector()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
AutoSizeAxes = Axes.X;
|
AutoSizeAxes = Axes.X;
|
||||||
|
}
|
||||||
|
|
||||||
Children = new[]
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AddRangeInternal(new[]
|
||||||
{
|
{
|
||||||
new OpaqueBackground(),
|
new OpaqueBackground
|
||||||
modeButtons = new FillFlowContainer
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y,
|
Depth = 1,
|
||||||
AutoSizeAxes = Axes.X,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
Padding = new MarginPadding { Left = padding, Right = padding },
|
|
||||||
},
|
},
|
||||||
modeButtonLine = new Container
|
modeButtonLine = new Container
|
||||||
{
|
{
|
||||||
@ -58,36 +50,50 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
Radius = 15,
|
Radius = 15,
|
||||||
Roundness = 15,
|
Roundness = 15,
|
||||||
},
|
},
|
||||||
Children = new[]
|
Child = new Box
|
||||||
{
|
{
|
||||||
new Box
|
RelativeSizeAxes = Axes.Both,
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
protected override void LoadComplete()
|
||||||
private void load(RulesetStore rulesets, Bindable<RulesetInfo> parentRuleset)
|
|
||||||
{
|
{
|
||||||
this.rulesets = rulesets;
|
base.LoadComplete();
|
||||||
|
|
||||||
foreach (var r in rulesets.AvailableRulesets)
|
Current.BindDisabledChanged(disabled => this.FadeColour(disabled ? Color4.Gray : Color4.White, 300), true);
|
||||||
{
|
Current.BindValueChanged(_ => moveLineToCurrent(), true);
|
||||||
modeButtons.Add(new ToolbarRulesetButton
|
|
||||||
{
|
|
||||||
Ruleset = r,
|
|
||||||
Action = delegate { ruleset.Value = r; }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ruleset.ValueChanged += rulesetChanged;
|
|
||||||
ruleset.DisabledChanged += disabledChanged;
|
|
||||||
ruleset.BindTo(parentRuleset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void moveLineToCurrent()
|
||||||
|
{
|
||||||
|
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
|
||||||
|
{
|
||||||
|
if (tabItem.Value == Current.Value)
|
||||||
|
{
|
||||||
|
modeButtonLine.MoveToX(tabItem.DrawPosition.X, 200, Easing.OutQuint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HandleNonPositionalInput => !Current.Disabled && base.HandleNonPositionalInput;
|
||||||
|
|
||||||
|
public override bool HandlePositionalInput => !Current.Disabled && base.HandlePositionalInput;
|
||||||
|
|
||||||
|
public override bool PropagatePositionalInputSubTree => !Current.Disabled && base.PropagatePositionalInputSubTree;
|
||||||
|
|
||||||
|
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new ToolbarRulesetTabButton(value);
|
||||||
|
|
||||||
|
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Padding = new MarginPadding { Left = padding, Right = padding },
|
||||||
|
};
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
{
|
{
|
||||||
base.OnKeyDown(e);
|
base.OnKeyDown(e);
|
||||||
@ -96,46 +102,13 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
{
|
{
|
||||||
int requested = e.Key - Key.Number1;
|
int requested = e.Key - Key.Number1;
|
||||||
|
|
||||||
RulesetInfo found = rulesets.AvailableRulesets.Skip(requested).FirstOrDefault();
|
RulesetInfo found = Rulesets.AvailableRulesets.Skip(requested).FirstOrDefault();
|
||||||
if (found != null)
|
if (found != null)
|
||||||
ruleset.Value = found;
|
Current.Value = found;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput;
|
|
||||||
public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput;
|
|
||||||
|
|
||||||
public override bool PropagatePositionalInputSubTree => !ruleset.Disabled && base.PropagatePositionalInputSubTree;
|
|
||||||
|
|
||||||
private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300);
|
|
||||||
|
|
||||||
private void rulesetChanged(ValueChangedEvent<RulesetInfo> e)
|
|
||||||
{
|
|
||||||
foreach (ToolbarRulesetButton m in modeButtons.Children.Cast<ToolbarRulesetButton>())
|
|
||||||
{
|
|
||||||
bool isActive = m.Ruleset.ID == e.NewValue.ID;
|
|
||||||
m.Active = isActive;
|
|
||||||
if (isActive)
|
|
||||||
activeButton = m;
|
|
||||||
}
|
|
||||||
|
|
||||||
activeMode.Invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Cached activeMode = new Cached();
|
|
||||||
|
|
||||||
protected override void UpdateAfterChildren()
|
|
||||||
{
|
|
||||||
base.UpdateAfterChildren();
|
|
||||||
|
|
||||||
if (!activeMode.IsValid)
|
|
||||||
{
|
|
||||||
modeButtonLine.MoveToX(activeButton.DrawPosition.X, 200, Easing.OutQuint);
|
|
||||||
activeMode.Validate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
76
osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs
Normal file
76
osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// 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.Effects;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Toolbar
|
||||||
|
{
|
||||||
|
public class ToolbarRulesetTabButton : TabItem<RulesetInfo>
|
||||||
|
{
|
||||||
|
private readonly RulesetButton ruleset;
|
||||||
|
|
||||||
|
public ToolbarRulesetTabButton(RulesetInfo value)
|
||||||
|
: base(value)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X;
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
Child = ruleset = new RulesetButton
|
||||||
|
{
|
||||||
|
Active = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
var rInstance = value.CreateInstance();
|
||||||
|
|
||||||
|
ruleset.TooltipMain = rInstance.Description;
|
||||||
|
ruleset.TooltipSub = $"Play some {rInstance.Description}";
|
||||||
|
ruleset.SetIcon(rInstance.CreateIcon());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnActivated() => ruleset.Active = true;
|
||||||
|
|
||||||
|
protected override void OnDeactivated() => ruleset.Active = false;
|
||||||
|
|
||||||
|
private class RulesetButton : ToolbarButton
|
||||||
|
{
|
||||||
|
public bool Active
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
IconContainer.Colour = Color4.White;
|
||||||
|
IconContainer.EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Colour = new Color4(255, 194, 224, 100),
|
||||||
|
Radius = 15,
|
||||||
|
Roundness = 15,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IconContainer.Colour = new Color4(255, 194, 224, 255);
|
||||||
|
IconContainer.EdgeEffect = new EdgeEffectParameters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e)
|
||||||
|
{
|
||||||
|
Parent.Click();
|
||||||
|
return base.OnClick(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
IconContainer.Scale *= 1.4f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,11 +4,11 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
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.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Overlays.Profile;
|
using osu.Game.Overlays.Profile;
|
||||||
using osu.Game.Overlays.Profile.Sections;
|
using osu.Game.Overlays.Profile.Sections;
|
||||||
@ -23,7 +23,7 @@ namespace osu.Game.Overlays
|
|||||||
private ProfileSection[] sections;
|
private ProfileSection[] sections;
|
||||||
private GetUserRequest userReq;
|
private GetUserRequest userReq;
|
||||||
protected ProfileHeader Header;
|
protected ProfileHeader Header;
|
||||||
private SectionsContainer<ProfileSection> sectionsContainer;
|
private ProfileSectionsContainer sectionsContainer;
|
||||||
private ProfileTabControl tabs;
|
private ProfileTabControl tabs;
|
||||||
|
|
||||||
public const float CONTENT_X_MARGIN = 70;
|
public const float CONTENT_X_MARGIN = 70;
|
||||||
@ -32,7 +32,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
public void ShowUser(User user, bool fetchOnline = true)
|
public void ShowUser(User user, bool fetchOnline = true)
|
||||||
{
|
{
|
||||||
if (user == User.SYSTEM_USER) return;
|
if (user == User.SYSTEM_USER)
|
||||||
|
return;
|
||||||
|
|
||||||
Show();
|
Show();
|
||||||
|
|
||||||
@ -65,19 +66,18 @@ namespace osu.Game.Overlays
|
|||||||
Add(new Box
|
Add(new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = OsuColour.Gray(0.2f)
|
Colour = OsuColour.Gray(0.1f)
|
||||||
});
|
});
|
||||||
|
|
||||||
Add(sectionsContainer = new SectionsContainer<ProfileSection>
|
Add(sectionsContainer = new ProfileSectionsContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
ExpandableHeader = Header = new ProfileHeader(),
|
ExpandableHeader = Header = new ProfileHeader(),
|
||||||
FixedHeader = tabs,
|
FixedHeader = tabs,
|
||||||
HeaderBackground = new Box
|
HeaderBackground = new Box
|
||||||
{
|
{
|
||||||
Colour = OsuColour.Gray(34),
|
Colour = OsuColour.Gray(34),
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
sectionsContainer.SelectedSection.ValueChanged += section =>
|
sectionsContainer.SelectedSection.ValueChanged += section =>
|
||||||
{
|
{
|
||||||
@ -141,31 +141,28 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ProfileTabControl : PageTabControl<ProfileSection>
|
private class ProfileTabControl : OverlayTabControl<ProfileSection>
|
||||||
{
|
{
|
||||||
private readonly Box bottom;
|
|
||||||
|
|
||||||
public ProfileTabControl()
|
public ProfileTabControl()
|
||||||
{
|
{
|
||||||
TabContainer.RelativeSizeAxes &= ~Axes.X;
|
TabContainer.RelativeSizeAxes &= ~Axes.X;
|
||||||
TabContainer.AutoSizeAxes |= Axes.X;
|
TabContainer.AutoSizeAxes |= Axes.X;
|
||||||
TabContainer.Anchor |= Anchor.x1;
|
TabContainer.Anchor |= Anchor.x1;
|
||||||
TabContainer.Origin |= Anchor.x1;
|
TabContainer.Origin |= Anchor.x1;
|
||||||
AddInternal(bottom = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = 1,
|
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
Origin = Anchor.BottomCentre,
|
|
||||||
EdgeSmoothness = new Vector2(1)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override TabItem<ProfileSection> CreateTabItem(ProfileSection value) => new ProfileTabItem(value);
|
protected override TabItem<ProfileSection> CreateTabItem(ProfileSection value) => new ProfileTabItem(value)
|
||||||
|
{
|
||||||
|
AccentColour = AccentColour
|
||||||
|
};
|
||||||
|
|
||||||
protected override Dropdown<ProfileSection> CreateDropdown() => null;
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
AccentColour = colours.Seafoam;
|
||||||
|
}
|
||||||
|
|
||||||
private class ProfileTabItem : PageTabItem
|
private class ProfileTabItem : OverlayTabItem<ProfileSection>
|
||||||
{
|
{
|
||||||
public ProfileTabItem(ProfileSection value)
|
public ProfileTabItem(ProfileSection value)
|
||||||
: base(value)
|
: base(value)
|
||||||
@ -173,12 +170,22 @@ namespace osu.Game.Overlays
|
|||||||
Text.Text = value.Title;
|
Text.Text = value.Title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
private class ProfileSectionsContainer : SectionsContainer<ProfileSection>
|
||||||
private void load(OsuColour colours)
|
{
|
||||||
|
public ProfileSectionsContainer()
|
||||||
{
|
{
|
||||||
bottom.Colour = colours.Yellow;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override FlowContainer<ProfileSection> CreateScrollContentContainer() => new FillFlowContainer<ProfileSection>
|
||||||
|
{
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Spacing = new Vector2(0, 20),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,10 @@ 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);
|
volumeMeterMaster.Decrease(amount, isPrecise);
|
||||||
return true;
|
return true;
|
||||||
@ -109,6 +113,10 @@ namespace osu.Game.Overlays
|
|||||||
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);
|
volumeMeterMaster.Increase(amount, isPrecise);
|
||||||
return true;
|
return true;
|
||||||
|
@ -77,10 +77,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
|
|
||||||
private bool judgementOccurred;
|
private bool judgementOccurred;
|
||||||
|
|
||||||
public bool Interactive = true;
|
|
||||||
public override bool HandleNonPositionalInput => Interactive;
|
|
||||||
public override bool HandlePositionalInput => Interactive;
|
|
||||||
|
|
||||||
public override bool RemoveWhenNotAlive => false;
|
public override bool RemoveWhenNotAlive => false;
|
||||||
public override bool RemoveCompletedTransforms => false;
|
public override bool RemoveCompletedTransforms => false;
|
||||||
protected override bool RequiresChildrenUpdate => true;
|
protected override bool RequiresChildrenUpdate => true;
|
||||||
|
23
osu.Game/Rulesets/RulesetSelector.cs
Normal file
23
osu.Game/Rulesets/RulesetSelector.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets
|
||||||
|
{
|
||||||
|
public abstract class RulesetSelector : TabControl<RulesetInfo>
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
protected RulesetStore Rulesets { get; private set; }
|
||||||
|
|
||||||
|
protected override Dropdown<RulesetInfo> CreateDropdown() => null;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
foreach (var r in Rulesets.AvailableRulesets)
|
||||||
|
AddItem(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -32,6 +32,8 @@ namespace osu.Game.Screens.Edit
|
|||||||
{
|
{
|
||||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
|
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
|
||||||
|
|
||||||
|
public override bool AllowBackButton => false;
|
||||||
|
|
||||||
public override bool HideOverlaysOnEnter => true;
|
public override bool HideOverlaysOnEnter => true;
|
||||||
|
|
||||||
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
||||||
|
@ -17,6 +17,11 @@ namespace osu.Game.Screens
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
bool DisallowExternalBeatmapRulesetChanges { get; }
|
bool DisallowExternalBeatmapRulesetChanges { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the user can exit this this <see cref="IOsuScreen"/> by pressing the back button.
|
||||||
|
/// </summary>
|
||||||
|
bool AllowBackButton { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether a top-level component should be allowed to exit the current screen to, for example,
|
/// Whether a top-level component should be allowed to exit the current screen to, for example,
|
||||||
/// complete an import. Note that this can be overridden by a user if they specifically request.
|
/// complete an import. Note that this can be overridden by a user if they specifically request.
|
||||||
|
@ -9,22 +9,13 @@ using osu.Framework.Graphics.Shaders;
|
|||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Overlays;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens
|
namespace osu.Game.Screens
|
||||||
{
|
{
|
||||||
public class Loader : OsuScreen
|
public class Loader : StartupScreen
|
||||||
{
|
{
|
||||||
private bool showDisclaimer;
|
private bool showDisclaimer;
|
||||||
|
|
||||||
public override bool HideOverlaysOnEnter => true;
|
|
||||||
|
|
||||||
public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
|
|
||||||
|
|
||||||
public override bool CursorVisible => false;
|
|
||||||
|
|
||||||
protected override bool AllowBackButton => false;
|
|
||||||
|
|
||||||
public Loader()
|
public Loader()
|
||||||
{
|
{
|
||||||
ValidForResume = false;
|
ValidForResume = false;
|
||||||
|
@ -15,12 +15,11 @@ using osu.Game.Graphics.Containers;
|
|||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Game.Overlays;
|
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Menu
|
namespace osu.Game.Screens.Menu
|
||||||
{
|
{
|
||||||
public class Disclaimer : OsuScreen
|
public class Disclaimer : StartupScreen
|
||||||
{
|
{
|
||||||
private Intro intro;
|
private Intro intro;
|
||||||
private SpriteIcon icon;
|
private SpriteIcon icon;
|
||||||
@ -28,11 +27,6 @@ namespace osu.Game.Screens.Menu
|
|||||||
private LinkFlowContainer textFlow;
|
private LinkFlowContainer textFlow;
|
||||||
private LinkFlowContainer supportFlow;
|
private LinkFlowContainer supportFlow;
|
||||||
|
|
||||||
public override bool HideOverlaysOnEnter => true;
|
|
||||||
public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
|
|
||||||
|
|
||||||
public override bool CursorVisible => false;
|
|
||||||
|
|
||||||
private Drawable heart;
|
private Drawable heart;
|
||||||
|
|
||||||
private const float icon_y = -85;
|
private const float icon_y = -85;
|
||||||
|
@ -15,11 +15,10 @@ using osu.Game.IO.Archives;
|
|||||||
using osu.Game.Screens.Backgrounds;
|
using osu.Game.Screens.Backgrounds;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Game.Overlays;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Menu
|
namespace osu.Game.Screens.Menu
|
||||||
{
|
{
|
||||||
public class Intro : OsuScreen
|
public class Intro : StartupScreen
|
||||||
{
|
{
|
||||||
private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83";
|
private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83";
|
||||||
|
|
||||||
@ -32,11 +31,6 @@ namespace osu.Game.Screens.Menu
|
|||||||
private SampleChannel welcome;
|
private SampleChannel welcome;
|
||||||
private SampleChannel seeya;
|
private SampleChannel seeya;
|
||||||
|
|
||||||
public override bool HideOverlaysOnEnter => true;
|
|
||||||
public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
|
|
||||||
|
|
||||||
public override bool CursorVisible => false;
|
|
||||||
|
|
||||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
|
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
|
||||||
|
|
||||||
private Bindable<bool> menuVoice;
|
private Bindable<bool> menuVoice;
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
public override bool HideOverlaysOnEnter => buttons == null || buttons.State == ButtonSystemState.Initial;
|
public override bool HideOverlaysOnEnter => buttons == null || buttons.State == ButtonSystemState.Initial;
|
||||||
|
|
||||||
protected override bool AllowBackButton => buttons.State != ButtonSystemState.Initial && host.CanExit;
|
public override bool AllowBackButton => false;
|
||||||
|
|
||||||
public override bool AllowExternalScreenChange => true;
|
public override bool AllowExternalScreenChange => true;
|
||||||
|
|
||||||
|
@ -54,11 +54,8 @@ namespace osu.Game.Screens.Multi.Match.Components
|
|||||||
hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID) != null;
|
hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void beatmapAdded(BeatmapSetInfo model, bool existing)
|
private void beatmapAdded(BeatmapSetInfo model)
|
||||||
{
|
{
|
||||||
if (Beatmap.Value == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (model.Beatmaps.Any(b => b.OnlineBeatmapID == Beatmap.Value.OnlineBeatmapID))
|
if (model.Beatmaps.Any(b => b.OnlineBeatmapID == Beatmap.Value.OnlineBeatmapID))
|
||||||
Schedule(() => hasBeatmap = true);
|
Schedule(() => hasBeatmap = true);
|
||||||
}
|
}
|
||||||
|
@ -195,6 +195,7 @@ namespace osu.Game.Screens.Multi.Match
|
|||||||
|
|
||||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
||||||
Mods.Value = e.NewValue?.RequiredMods?.ToArray() ?? Array.Empty<Mod>();
|
Mods.Value = e.NewValue?.RequiredMods?.ToArray() ?? Array.Empty<Mod>();
|
||||||
|
|
||||||
if (e.NewValue?.Ruleset != null)
|
if (e.NewValue?.Ruleset != null)
|
||||||
Ruleset.Value = e.NewValue.Ruleset;
|
Ruleset.Value = e.NewValue.Ruleset;
|
||||||
}
|
}
|
||||||
@ -202,7 +203,7 @@ namespace osu.Game.Screens.Multi.Match
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle the case where a beatmap is imported (and can be used by this match).
|
/// Handle the case where a beatmap is imported (and can be used by this match).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void beatmapAdded(BeatmapSetInfo model, bool existing) => Schedule(() =>
|
private void beatmapAdded(BeatmapSetInfo model) => Schedule(() =>
|
||||||
{
|
{
|
||||||
if (Beatmap.Value != beatmapManager.DefaultBeatmap)
|
if (Beatmap.Value != beatmapManager.DefaultBeatmap)
|
||||||
return;
|
return;
|
||||||
|
@ -212,6 +212,12 @@ namespace osu.Game.Screens.Multi
|
|||||||
|
|
||||||
public override bool OnExiting(IScreen next)
|
public override bool OnExiting(IScreen next)
|
||||||
{
|
{
|
||||||
|
if (!(screenStack.CurrentScreen is LoungeSubScreen))
|
||||||
|
{
|
||||||
|
screenStack.Exit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
waves.Hide();
|
waves.Hide();
|
||||||
|
|
||||||
this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut();
|
this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut();
|
||||||
@ -257,7 +263,7 @@ namespace osu.Game.Screens.Multi
|
|||||||
private void subScreenChanged(IScreen newScreen)
|
private void subScreenChanged(IScreen newScreen)
|
||||||
{
|
{
|
||||||
updatePollingRate(isIdle.Value);
|
updatePollingRate(isIdle.Value);
|
||||||
createButton.FadeTo(newScreen is MatchSubScreen ? 0 : 1, 200);
|
createButton.FadeTo(newScreen is LoungeSubScreen ? 1 : 0, 200);
|
||||||
|
|
||||||
updateTrack();
|
updateTrack();
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,8 @@ 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;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Bindings;
|
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Input.Bindings;
|
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
@ -21,7 +19,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
|
|
||||||
namespace osu.Game.Screens
|
namespace osu.Game.Screens
|
||||||
{
|
{
|
||||||
public abstract class OsuScreen : Screen, IOsuScreen, IKeyBindingHandler<GlobalAction>, IHasDescription
|
public abstract class OsuScreen : Screen, IOsuScreen, IHasDescription
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of negative padding that should be applied to game background content which touches both the left and right sides of the screen.
|
/// The amount of negative padding that should be applied to game background content which touches both the left and right sides of the screen.
|
||||||
@ -36,7 +34,7 @@ namespace osu.Game.Screens
|
|||||||
|
|
||||||
public string Description => Title;
|
public string Description => Title;
|
||||||
|
|
||||||
protected virtual bool AllowBackButton => true;
|
public virtual bool AllowBackButton => true;
|
||||||
|
|
||||||
public virtual bool AllowExternalScreenChange => false;
|
public virtual bool AllowExternalScreenChange => false;
|
||||||
|
|
||||||
@ -131,21 +129,6 @@ namespace osu.Game.Screens
|
|||||||
sampleExit = audio.Samples.Get(@"UI/screen-back");
|
sampleExit = audio.Samples.Get(@"UI/screen-back");
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool OnPressed(GlobalAction action)
|
|
||||||
{
|
|
||||||
if (!this.IsCurrentScreen()) return false;
|
|
||||||
|
|
||||||
if (action == GlobalAction.Back && AllowBackButton)
|
|
||||||
{
|
|
||||||
this.Exit();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OnReleased(GlobalAction action) => action == GlobalAction.Back && AllowBackButton;
|
|
||||||
|
|
||||||
public override void OnResuming(IScreen last)
|
public override void OnResuming(IScreen last)
|
||||||
{
|
{
|
||||||
if (PlayResumeSound)
|
if (PlayResumeSound)
|
||||||
|
28
osu.Game/Screens/Play/HotkeyExitOverlay.cs
Normal file
28
osu.Game/Screens/Play/HotkeyExitOverlay.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// 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.Input.Bindings;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play
|
||||||
|
{
|
||||||
|
public class HotkeyExitOverlay : HoldToConfirmOverlay, IKeyBindingHandler<GlobalAction>
|
||||||
|
{
|
||||||
|
public bool OnPressed(GlobalAction action)
|
||||||
|
{
|
||||||
|
if (action != GlobalAction.QuickExit) return false;
|
||||||
|
|
||||||
|
BeginConfirm();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(GlobalAction action)
|
||||||
|
{
|
||||||
|
if (action != GlobalAction.QuickExit) return false;
|
||||||
|
|
||||||
|
AbortConfirm();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
public class Player : ScreenWithBeatmapBackground
|
public class Player : ScreenWithBeatmapBackground
|
||||||
{
|
{
|
||||||
protected override bool AllowBackButton => false; // handled by HoldForMenuButton
|
public override bool AllowBackButton => false; // handled by HoldForMenuButton
|
||||||
|
|
||||||
protected override UserActivity InitialActivity => new UserActivity.SoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value);
|
protected override UserActivity InitialActivity => new UserActivity.SoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value);
|
||||||
|
|
||||||
@ -177,6 +177,16 @@ namespace osu.Game.Screens.Play
|
|||||||
Restart();
|
Restart();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
new HotkeyExitOverlay
|
||||||
|
{
|
||||||
|
Action = () =>
|
||||||
|
{
|
||||||
|
if (!this.IsCurrentScreen()) return;
|
||||||
|
|
||||||
|
fadeOut(true);
|
||||||
|
performImmediateExit();
|
||||||
|
},
|
||||||
|
},
|
||||||
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }
|
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -241,6 +251,16 @@ namespace osu.Game.Screens.Play
|
|||||||
return working;
|
return working;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void performImmediateExit()
|
||||||
|
{
|
||||||
|
// if a restart has been requested, cancel any pending completion (user has shown intent to restart).
|
||||||
|
onCompletionEvent = null;
|
||||||
|
|
||||||
|
ValidForResume = false;
|
||||||
|
|
||||||
|
performUserRequestedExit();
|
||||||
|
}
|
||||||
|
|
||||||
private void performUserRequestedExit()
|
private void performUserRequestedExit()
|
||||||
{
|
{
|
||||||
if (!this.IsCurrentScreen()) return;
|
if (!this.IsCurrentScreen()) return;
|
||||||
|
@ -247,6 +247,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public override void OnSuspending(IScreen next)
|
public override void OnSuspending(IScreen next)
|
||||||
{
|
{
|
||||||
|
BackgroundBrightnessReduction = false;
|
||||||
base.OnSuspending(next);
|
base.OnSuspending(next);
|
||||||
cancelLoad();
|
cancelLoad();
|
||||||
}
|
}
|
||||||
@ -258,6 +259,7 @@ namespace osu.Game.Screens.Play
|
|||||||
cancelLoad();
|
cancelLoad();
|
||||||
|
|
||||||
Background.EnableUserDim.Value = false;
|
Background.EnableUserDim.Value = false;
|
||||||
|
BackgroundBrightnessReduction = false;
|
||||||
|
|
||||||
return base.OnExiting(next);
|
return base.OnExiting(next);
|
||||||
}
|
}
|
||||||
@ -273,6 +275,22 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool backgroundBrightnessReduction;
|
||||||
|
|
||||||
|
protected bool BackgroundBrightnessReduction
|
||||||
|
{
|
||||||
|
get => backgroundBrightnessReduction;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == backgroundBrightnessReduction)
|
||||||
|
return;
|
||||||
|
|
||||||
|
backgroundBrightnessReduction = value;
|
||||||
|
|
||||||
|
Background.FadeColour(OsuColour.Gray(backgroundBrightnessReduction ? 0.8f : 1), 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
@ -287,12 +305,16 @@ namespace osu.Game.Screens.Play
|
|||||||
// Preview user-defined background dim and blur when hovered on the visual settings panel.
|
// Preview user-defined background dim and blur when hovered on the visual settings panel.
|
||||||
Background.EnableUserDim.Value = true;
|
Background.EnableUserDim.Value = true;
|
||||||
Background.BlurAmount.Value = 0;
|
Background.BlurAmount.Value = 0;
|
||||||
|
|
||||||
|
BackgroundBrightnessReduction = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Returns background dim and blur to the values specified by PlayerLoader.
|
// Returns background dim and blur to the values specified by PlayerLoader.
|
||||||
Background.EnableUserDim.Value = false;
|
Background.EnableUserDim.Value = false;
|
||||||
Background.BlurAmount.Value = BACKGROUND_BLUR;
|
Background.BlurAmount.Value = BACKGROUND_BLUR;
|
||||||
|
|
||||||
|
BackgroundBrightnessReduction = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ using osu.Game.Screens.Backgrounds;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -254,13 +253,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
new BackButton
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Action = this.Exit
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var t in CreateResultPages())
|
foreach (var t in CreateResultPages())
|
||||||
|
@ -20,8 +20,6 @@ namespace osu.Game.Screens
|
|||||||
{
|
{
|
||||||
public class ScreenWhiteBox : OsuScreen
|
public class ScreenWhiteBox : OsuScreen
|
||||||
{
|
{
|
||||||
private readonly BackButton popButton;
|
|
||||||
|
|
||||||
private const double transition_time = 1000;
|
private const double transition_time = 1000;
|
||||||
|
|
||||||
protected virtual IEnumerable<Type> PossibleChildren => null;
|
protected virtual IEnumerable<Type> PossibleChildren => null;
|
||||||
@ -35,10 +33,6 @@ namespace osu.Game.Screens
|
|||||||
{
|
{
|
||||||
base.OnEntering(last);
|
base.OnEntering(last);
|
||||||
|
|
||||||
//only show the pop button if we are entered form another screen.
|
|
||||||
if (last != null)
|
|
||||||
popButton.Alpha = 1;
|
|
||||||
|
|
||||||
Alpha = 0;
|
Alpha = 0;
|
||||||
textContainer.Position = new Vector2(DrawSize.X / 16, 0);
|
textContainer.Position = new Vector2(DrawSize.X / 16, 0);
|
||||||
|
|
||||||
@ -144,13 +138,6 @@ namespace osu.Game.Screens
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
popButton = new BackButton
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Alpha = 0,
|
|
||||||
Action = this.Exit
|
|
||||||
},
|
|
||||||
childModeButtons = new FillFlowContainer
|
childModeButtons = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
|
@ -148,40 +148,37 @@ namespace osu.Game.Screens.Select
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet)
|
public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
|
||||||
{
|
{
|
||||||
Schedule(() =>
|
int? previouslySelectedID = null;
|
||||||
|
CarouselBeatmapSet existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID);
|
||||||
|
|
||||||
|
// If the selected beatmap is about to be removed, store its ID so it can be re-selected if required
|
||||||
|
if (existingSet?.State?.Value == CarouselItemState.Selected)
|
||||||
|
previouslySelectedID = selectedBeatmap?.Beatmap.ID;
|
||||||
|
|
||||||
|
var newSet = createCarouselSet(beatmapSet);
|
||||||
|
|
||||||
|
if (existingSet != null)
|
||||||
|
root.RemoveChild(existingSet);
|
||||||
|
|
||||||
|
if (newSet == null)
|
||||||
{
|
{
|
||||||
int? previouslySelectedID = null;
|
|
||||||
CarouselBeatmapSet existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID);
|
|
||||||
|
|
||||||
// If the selected beatmap is about to be removed, store its ID so it can be re-selected if required
|
|
||||||
if (existingSet?.State?.Value == CarouselItemState.Selected)
|
|
||||||
previouslySelectedID = selectedBeatmap?.Beatmap.ID;
|
|
||||||
|
|
||||||
var newSet = createCarouselSet(beatmapSet);
|
|
||||||
|
|
||||||
if (existingSet != null)
|
|
||||||
root.RemoveChild(existingSet);
|
|
||||||
|
|
||||||
if (newSet == null)
|
|
||||||
{
|
|
||||||
itemsCache.Invalidate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
root.AddChild(newSet);
|
|
||||||
|
|
||||||
applyActiveCriteria(false, false);
|
|
||||||
|
|
||||||
//check if we can/need to maintain our current selection.
|
|
||||||
if (previouslySelectedID != null)
|
|
||||||
select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == previouslySelectedID) ?? newSet);
|
|
||||||
|
|
||||||
itemsCache.Invalidate();
|
itemsCache.Invalidate();
|
||||||
Schedule(() => BeatmapSetsChanged?.Invoke());
|
return;
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
root.AddChild(newSet);
|
||||||
|
|
||||||
|
applyActiveCriteria(false, false);
|
||||||
|
|
||||||
|
//check if we can/need to maintain our current selection.
|
||||||
|
if (previouslySelectedID != null)
|
||||||
|
select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == previouslySelectedID) ?? newSet);
|
||||||
|
|
||||||
|
itemsCache.Invalidate();
|
||||||
|
Schedule(() => BeatmapSetsChanged?.Invoke());
|
||||||
|
});
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects a given beatmap on the carousel.
|
/// Selects a given beatmap on the carousel.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -25,8 +24,6 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private const float padding = 80;
|
private const float padding = 80;
|
||||||
|
|
||||||
public Action OnBack;
|
|
||||||
|
|
||||||
private readonly FillFlowContainer<FooterButton> buttons;
|
private readonly FillFlowContainer<FooterButton> buttons;
|
||||||
|
|
||||||
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
|
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
|
||||||
@ -83,12 +80,6 @@ namespace osu.Game.Screens.Select
|
|||||||
Height = 3,
|
Height = 3,
|
||||||
Position = new Vector2(0, -3),
|
Position = new Vector2(0, -3),
|
||||||
},
|
},
|
||||||
new BackButton
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Action = () => OnBack?.Invoke()
|
|
||||||
},
|
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
|
@ -34,10 +34,11 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select
|
namespace osu.Game.Screens.Select
|
||||||
{
|
{
|
||||||
public abstract class SongSelect : OsuScreen
|
public abstract class SongSelect : OsuScreen, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245);
|
private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245);
|
||||||
|
|
||||||
@ -186,31 +187,27 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
if (ShowFooter)
|
if (ShowFooter)
|
||||||
{
|
{
|
||||||
AddInternal(FooterPanels = new Container
|
AddRangeInternal(new[]
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
FooterPanels = new Container
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Margin = new MarginPadding
|
|
||||||
{
|
|
||||||
Bottom = Footer.HEIGHT,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
AddInternal(Footer = new Footer
|
|
||||||
{
|
|
||||||
OnBack = ExitFromBack,
|
|
||||||
});
|
|
||||||
|
|
||||||
FooterPanels.AddRange(new Drawable[]
|
|
||||||
{
|
|
||||||
BeatmapOptions = new BeatmapOptionsOverlay(),
|
|
||||||
ModSelect = new ModSelectOverlay
|
|
||||||
{
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Origin = Anchor.BottomCentre,
|
AutoSizeAxes = Axes.Y,
|
||||||
Anchor = Anchor.BottomCentre,
|
Margin = new MarginPadding { Bottom = Footer.HEIGHT },
|
||||||
}
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
BeatmapOptions = new BeatmapOptionsOverlay(),
|
||||||
|
ModSelect = new ModSelectOverlay
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Footer = new Footer()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,17 +274,6 @@ namespace osu.Game.Screens.Select
|
|||||||
return dependencies;
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void ExitFromBack()
|
|
||||||
{
|
|
||||||
if (ModSelect.State.Value == Visibility.Visible)
|
|
||||||
{
|
|
||||||
ModSelect.Hide();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Edit(BeatmapInfo beatmap = null)
|
public void Edit(BeatmapInfo beatmap = null)
|
||||||
{
|
{
|
||||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap ?? beatmapNoDebounce);
|
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap ?? beatmapNoDebounce);
|
||||||
@ -518,6 +504,12 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
public override bool OnExiting(IScreen next)
|
public override bool OnExiting(IScreen next)
|
||||||
{
|
{
|
||||||
|
if (ModSelect.State.Value == Visibility.Visible)
|
||||||
|
{
|
||||||
|
ModSelect.Hide();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (base.OnExiting(next))
|
if (base.OnExiting(next))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -586,7 +578,7 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onBeatmapSetAdded(BeatmapSetInfo s, bool existing) => Carousel.UpdateBeatmapSet(s);
|
private void onBeatmapSetAdded(BeatmapSetInfo s) => Carousel.UpdateBeatmapSet(s);
|
||||||
private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s);
|
private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s);
|
||||||
private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
||||||
private void onBeatmapHidden(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
private void onBeatmapHidden(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
||||||
@ -649,7 +641,7 @@ namespace osu.Game.Screens.Select
|
|||||||
Schedule(() => BeatmapDetails.Leaderboard.RefreshScores())));
|
Schedule(() => BeatmapDetails.Leaderboard.RefreshScores())));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnPressed(GlobalAction action)
|
public virtual bool OnPressed(GlobalAction action)
|
||||||
{
|
{
|
||||||
if (!this.IsCurrentScreen()) return false;
|
if (!this.IsCurrentScreen()) return false;
|
||||||
|
|
||||||
@ -660,9 +652,11 @@ namespace osu.Game.Screens.Select
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnPressed(action);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(GlobalAction action) => action == GlobalAction.Select;
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
{
|
{
|
||||||
if (e.Repeat) return false;
|
if (e.Repeat) return false;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user