mirror of
https://github.com/ppy/osu.git
synced 2025-02-22 18:12:56 +08:00
Merge branch 'master' into editor-slider-control-point-quick-delete
This commit is contained in:
commit
ea2fd831ab
2
.gitignore
vendored
2
.gitignore
vendored
@ -334,3 +334,5 @@ inspectcode
|
|||||||
|
|
||||||
# BenchmarkDotNet
|
# BenchmarkDotNet
|
||||||
/BenchmarkDotNet.Artifacts
|
/BenchmarkDotNet.Artifacts
|
||||||
|
|
||||||
|
*.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
@ -4,5 +4,6 @@ M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(
|
|||||||
M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead.
|
M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead.
|
||||||
T:System.IComparable;Don't use non-generic IComparable. Use generic version instead.
|
T:System.IComparable;Don't use non-generic IComparable. Use generic version instead.
|
||||||
M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
|
M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
|
||||||
|
M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900)
|
||||||
T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods.
|
T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods.
|
||||||
T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods.
|
T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods.
|
||||||
|
@ -62,6 +62,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
|
|
||||||
public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
|
public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
|
||||||
{
|
{
|
||||||
|
protected override double InitialLifetimeOffset => HitObject.TimePreempt;
|
||||||
|
|
||||||
public virtual bool StaysOnPlate => HitObject.CanBePlated;
|
public virtual bool StaysOnPlate => HitObject.CanBePlated;
|
||||||
|
|
||||||
public float DisplayRadius => DrawSize.X / 2 * Scale.X * HitObject.Scale;
|
public float DisplayRadius => DrawSize.X / 2 * Scale.X * HitObject.Scale;
|
||||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
if (Position > lastCatchFrame.Position)
|
if (Position > lastCatchFrame.Position)
|
||||||
lastCatchFrame.Actions.Add(CatchAction.MoveRight);
|
lastCatchFrame.Actions.Add(CatchAction.MoveRight);
|
||||||
else if (Position < lastCatchFrame.Position)
|
else if (Position < lastCatchFrame.Position)
|
||||||
Actions.Add(CatchAction.MoveLeft);
|
lastCatchFrame.Actions.Add(CatchAction.MoveLeft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Audio
|
|||||||
private readonly ControlPointInfo controlPoints;
|
private readonly ControlPointInfo controlPoints;
|
||||||
private readonly Dictionary<double, DrumSample> mappings = new Dictionary<double, DrumSample>();
|
private readonly Dictionary<double, DrumSample> mappings = new Dictionary<double, DrumSample>();
|
||||||
|
|
||||||
private IBindableList<SampleControlPoint> samplePoints;
|
private readonly IBindableList<SampleControlPoint> samplePoints = new BindableList<SampleControlPoint>();
|
||||||
|
|
||||||
public DrumSampleContainer(ControlPointInfo controlPoints)
|
public DrumSampleContainer(ControlPointInfo controlPoints)
|
||||||
{
|
{
|
||||||
@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Audio
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
samplePoints = controlPoints.SamplePoints.GetBoundCopy();
|
samplePoints.BindTo(controlPoints.SamplePoints);
|
||||||
samplePoints.BindCollectionChanged((_, __) => recreateMappings(), true);
|
samplePoints.BindCollectionChanged((_, __) => recreateMappings(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddToggleStep("toggle y", state => selectionBox.CanScaleY = state);
|
AddToggleStep("toggle y", state => selectionBox.CanScaleY = state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleScale(Vector2 amount, Anchor reference)
|
private bool handleScale(Vector2 amount, Anchor reference)
|
||||||
{
|
{
|
||||||
if ((reference & Anchor.y1) == 0)
|
if ((reference & Anchor.y1) == 0)
|
||||||
{
|
{
|
||||||
@ -58,12 +58,15 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
selectionArea.X += amount.X;
|
selectionArea.X += amount.X;
|
||||||
selectionArea.Width += directionX * amount.X;
|
selectionArea.Width += directionX * amount.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleRotation(float angle)
|
private bool handleRotation(float angle)
|
||||||
{
|
{
|
||||||
// kinda silly and wrong, but just showing that the drag handles work.
|
// kinda silly and wrong, but just showing that the drag handles work.
|
||||||
selectionArea.Rotation += angle;
|
selectionArea.Rotation += angle;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,14 @@ using osu.Framework.Graphics.Colour;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Cursor;
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editing
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
@ -88,6 +89,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
// Scroll in at 0.25
|
// Scroll in at 0.25
|
||||||
AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
|
AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
|
||||||
|
AddStep("Press alt down", () => InputManager.PressKey(Key.AltLeft));
|
||||||
AddStep("Scroll by 3", () => InputManager.ScrollBy(new Vector2(0, 3)));
|
AddStep("Scroll by 3", () => InputManager.ScrollBy(new Vector2(0, 3)));
|
||||||
AddAssert("Box not at 0", () => !Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
|
AddAssert("Box not at 0", () => !Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
|
||||||
AddAssert("Box 1/4 at 1/4", () => Precision.AlmostEquals(boxQuad.TopLeft.X + 0.25f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X));
|
AddAssert("Box 1/4 at 1/4", () => Precision.AlmostEquals(boxQuad.TopLeft.X + 0.25f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X));
|
||||||
@ -96,6 +98,25 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("Scroll by -3", () => InputManager.ScrollBy(new Vector2(0, -3)));
|
AddStep("Scroll by -3", () => InputManager.ScrollBy(new Vector2(0, -3)));
|
||||||
AddAssert("Box at 0", () => Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
|
AddAssert("Box at 0", () => Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
|
||||||
AddAssert("Box 1/4 at 1/4", () => Precision.AlmostEquals(boxQuad.TopLeft.X + 0.25f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X));
|
AddAssert("Box 1/4 at 1/4", () => Precision.AlmostEquals(boxQuad.TopLeft.X + 0.25f * boxQuad.Size.X, scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X));
|
||||||
|
AddStep("Release alt", () => InputManager.ReleaseKey(Key.AltLeft));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMouseZoomInThenScroll()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
|
||||||
|
// Scroll in at 0.25
|
||||||
|
AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
|
||||||
|
AddStep("Press alt down", () => InputManager.PressKey(Key.AltLeft));
|
||||||
|
AddStep("Zoom by 3", () => InputManager.ScrollBy(new Vector2(0, 3)));
|
||||||
|
AddStep("Release alt", () => InputManager.ReleaseKey(Key.AltLeft));
|
||||||
|
|
||||||
|
AddStep("Scroll far left", () => InputManager.ScrollBy(new Vector2(0, 30)));
|
||||||
|
AddUntilStep("Scroll is at start", () => Precision.AlmostEquals(scrollQuad.TopLeft.X, boxQuad.TopLeft.X, 1));
|
||||||
|
|
||||||
|
AddStep("Scroll far right", () => InputManager.ScrollBy(new Vector2(0, -300)));
|
||||||
|
AddUntilStep("Scroll is at end", () => Precision.AlmostEquals(scrollQuad.TopRight.X, boxQuad.TopRight.X, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -103,6 +124,8 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
|
AddStep("Press alt down", () => InputManager.PressKey(Key.AltLeft));
|
||||||
|
|
||||||
// Scroll in at 0.25
|
// Scroll in at 0.25
|
||||||
AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
|
AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
|
||||||
AddStep("Scroll by 1", () => InputManager.ScrollBy(new Vector2(0, 1)));
|
AddStep("Scroll by 1", () => InputManager.ScrollBy(new Vector2(0, 1)));
|
||||||
@ -124,6 +147,8 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
|
AddStep("Move mouse to 0.25x", () => InputManager.MoveMouseTo(new Vector2(scrollQuad.TopLeft.X + 0.25f * scrollQuad.Size.X, scrollQuad.Centre.Y)));
|
||||||
AddStep("Scroll by -1", () => InputManager.ScrollBy(new Vector2(0, -1)));
|
AddStep("Scroll by -1", () => InputManager.ScrollBy(new Vector2(0, -1)));
|
||||||
AddAssert("Box at 0", () => Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
|
AddAssert("Box at 0", () => Precision.AlmostEquals(boxQuad.TopLeft, scrollQuad.TopLeft));
|
||||||
|
|
||||||
|
AddStep("Release alt", () => InputManager.ReleaseKey(Key.AltLeft));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reset()
|
private void reset()
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
@ -227,12 +229,19 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded);
|
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class TestSpectatorStreamingClient : SpectatorStreamingClient
|
public class TestSpectatorStreamingClient : SpectatorStreamingClient
|
||||||
{
|
{
|
||||||
public readonly User StreamingUser = new User { Id = 1234, Username = "Test user" };
|
public readonly User StreamingUser = new User { Id = 1234, Username = "Test user" };
|
||||||
|
|
||||||
|
public new BindableList<int> PlayingUsers => (BindableList<int>)base.PlayingUsers;
|
||||||
|
|
||||||
private int beatmapId;
|
private int beatmapId;
|
||||||
|
|
||||||
|
protected override Task Connect()
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
public void StartPlay(int beatmapId)
|
public void StartPlay(int beatmapId)
|
||||||
{
|
{
|
||||||
this.beatmapId = beatmapId;
|
this.beatmapId = beatmapId;
|
||||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private Replay replay;
|
private Replay replay;
|
||||||
|
|
||||||
private IBindableList<int> users;
|
private readonly IBindableList<int> users = new BindableList<int>();
|
||||||
|
|
||||||
private TestReplayRecorder recorder;
|
private TestReplayRecorder recorder;
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
replay = new Replay();
|
replay = new Replay();
|
||||||
|
|
||||||
users = streamingClient.PlayingUsers.GetBoundCopy();
|
users.BindTo(streamingClient.PlayingUsers);
|
||||||
users.BindCollectionChanged((obj, args) =>
|
users.BindCollectionChanged((obj, args) =>
|
||||||
{
|
{
|
||||||
switch (args.Action)
|
switch (args.Action)
|
||||||
|
@ -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 System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Online.Spectator;
|
||||||
|
using osu.Game.Overlays.Dashboard;
|
||||||
|
using osu.Game.Tests.Visual.Gameplay;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public class TestSceneCurrentlyPlayingDisplay : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached(typeof(SpectatorStreamingClient))]
|
||||||
|
private TestSceneSpectator.TestSpectatorStreamingClient testSpectatorStreamingClient = new TestSceneSpectator.TestSpectatorStreamingClient();
|
||||||
|
|
||||||
|
private CurrentlyPlayingDisplay currentlyPlaying;
|
||||||
|
|
||||||
|
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("register request handling", () => dummyAPI.HandleRequest = req =>
|
||||||
|
{
|
||||||
|
switch (req)
|
||||||
|
{
|
||||||
|
case GetUserRequest cRequest:
|
||||||
|
cRequest.TriggerSuccess(new User { Username = "peppy", Id = 2 });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("add streaming client", () =>
|
||||||
|
{
|
||||||
|
Remove(testSpectatorStreamingClient);
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
testSpectatorStreamingClient,
|
||||||
|
currentlyPlaying = new CurrentlyPlayingDisplay
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("Reset players", () => testSpectatorStreamingClient.PlayingUsers.Clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasicDisplay()
|
||||||
|
{
|
||||||
|
AddStep("Add playing user", () => testSpectatorStreamingClient.PlayingUsers.Add(2));
|
||||||
|
AddUntilStep("Panel loaded", () => currentlyPlaying.ChildrenOfType<UserGridPanel>()?.FirstOrDefault()?.User.Id == 2);
|
||||||
|
AddStep("Remove playing user", () => testSpectatorStreamingClient.PlayingUsers.Remove(2));
|
||||||
|
AddUntilStep("Panel no longer present", () => !currentlyPlaying.ChildrenOfType<UserGridPanel>().Any());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -42,6 +42,8 @@ namespace osu.Game.Configuration
|
|||||||
Set(OsuSetting.Username, string.Empty);
|
Set(OsuSetting.Username, string.Empty);
|
||||||
Set(OsuSetting.Token, string.Empty);
|
Set(OsuSetting.Token, string.Empty);
|
||||||
|
|
||||||
|
Set(OsuSetting.AutomaticallyDownloadWhenSpectating, false);
|
||||||
|
|
||||||
Set(OsuSetting.SavePassword, false).ValueChanged += enabled =>
|
Set(OsuSetting.SavePassword, false).ValueChanged += enabled =>
|
||||||
{
|
{
|
||||||
if (enabled.NewValue) Set(OsuSetting.SaveUsername, true);
|
if (enabled.NewValue) Set(OsuSetting.SaveUsername, true);
|
||||||
@ -132,6 +134,8 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
Set(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin);
|
Set(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin);
|
||||||
Set(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes);
|
Set(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Sometimes);
|
||||||
|
|
||||||
|
Set(OsuSetting.EditorWaveformOpacity, 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsuConfigManager(Storage storage)
|
public OsuConfigManager(Storage storage)
|
||||||
@ -241,6 +245,8 @@ namespace osu.Game.Configuration
|
|||||||
HitLighting,
|
HitLighting,
|
||||||
MenuBackgroundSource,
|
MenuBackgroundSource,
|
||||||
GameplayDisableWinKey,
|
GameplayDisableWinKey,
|
||||||
SeasonalBackgroundMode
|
SeasonalBackgroundMode,
|
||||||
|
EditorWaveformOpacity,
|
||||||
|
AutomaticallyDownloadWhenSpectating,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,14 +93,14 @@ namespace osu.Game.Online.Spectator
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case APIState.Online:
|
case APIState.Online:
|
||||||
Task.Run(connect);
|
Task.Run(Connect);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string endpoint = "https://spectator.ppy.sh/spectator";
|
private const string endpoint = "https://spectator.ppy.sh/spectator";
|
||||||
|
|
||||||
private async Task connect()
|
protected virtual async Task Connect()
|
||||||
{
|
{
|
||||||
if (connection != null)
|
if (connection != null)
|
||||||
return;
|
return;
|
||||||
|
132
osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs
Normal file
132
osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Online.Spectator;
|
||||||
|
using osu.Game.Screens.Multi.Match.Components;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Dashboard
|
||||||
|
{
|
||||||
|
internal class CurrentlyPlayingDisplay : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly IBindableList<int> playingUsers = new BindableList<int>();
|
||||||
|
|
||||||
|
private FillFlowContainer<PlayingUserPanel> userFlow;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private SpectatorStreamingClient spectatorStreaming { get; set; }
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
InternalChild = userFlow = new FillFlowContainer<PlayingUserPanel>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Spacing = new Vector2(10),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
playingUsers.BindTo(spectatorStreaming.PlayingUsers);
|
||||||
|
playingUsers.BindCollectionChanged((sender, e) => Schedule(() =>
|
||||||
|
{
|
||||||
|
switch (e.Action)
|
||||||
|
{
|
||||||
|
case NotifyCollectionChangedAction.Add:
|
||||||
|
foreach (var u in e.NewItems.OfType<int>())
|
||||||
|
{
|
||||||
|
var request = new GetUserRequest(u);
|
||||||
|
request.Success += user => Schedule(() =>
|
||||||
|
{
|
||||||
|
if (playingUsers.Contains((int)user.Id))
|
||||||
|
userFlow.Add(createUserPanel(user));
|
||||||
|
});
|
||||||
|
api.Queue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NotifyCollectionChangedAction.Remove:
|
||||||
|
foreach (var u in e.OldItems.OfType<int>())
|
||||||
|
userFlow.FirstOrDefault(card => card.User.Id == u)?.Expire();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NotifyCollectionChangedAction.Reset:
|
||||||
|
userFlow.Clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayingUserPanel createUserPanel(User user) =>
|
||||||
|
new PlayingUserPanel(user).With(panel =>
|
||||||
|
{
|
||||||
|
panel.Anchor = Anchor.TopCentre;
|
||||||
|
panel.Origin = Anchor.TopCentre;
|
||||||
|
});
|
||||||
|
|
||||||
|
private class PlayingUserPanel : CompositeDrawable
|
||||||
|
{
|
||||||
|
public readonly User User;
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private OsuGame game { get; set; }
|
||||||
|
|
||||||
|
public PlayingUserPanel(User user)
|
||||||
|
{
|
||||||
|
User = user;
|
||||||
|
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(2),
|
||||||
|
Width = 290,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new UserGridPanel(user)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
},
|
||||||
|
new PurpleTriangleButton
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Text = "Watch",
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Action = () => game?.PerformFromScreen(s => s.Push(new Spectator(user)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Dashboard
|
namespace osu.Game.Overlays.Dashboard
|
||||||
{
|
{
|
||||||
public class DashboardOverlayHeader : TabControlOverlayHeader<DashboardOverlayTabs>
|
public class DashboardOverlayHeader : TabControlOverlayHeader<DashboardOverlayTabs>
|
||||||
@ -20,6 +22,9 @@ namespace osu.Game.Overlays.Dashboard
|
|||||||
|
|
||||||
public enum DashboardOverlayTabs
|
public enum DashboardOverlayTabs
|
||||||
{
|
{
|
||||||
Friends
|
Friends,
|
||||||
|
|
||||||
|
[Description("Currently Playing")]
|
||||||
|
CurrentlyPlaying
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,6 +130,11 @@ namespace osu.Game.Overlays
|
|||||||
loadDisplay(new FriendDisplay());
|
loadDisplay(new FriendDisplay());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DashboardOverlayTabs.CurrentlyPlaying:
|
||||||
|
//todo: enable once caching logic is better
|
||||||
|
//loadDisplay(new CurrentlyPlayingDisplay());
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Display for {tab.NewValue} tab is not implemented");
|
throw new NotImplementedException($"Display for {tab.NewValue} tab is not implemented");
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,12 @@ namespace osu.Game.Overlays.Settings.Sections.Online
|
|||||||
Keywords = new[] { "no-video" },
|
Keywords = new[] { "no-video" },
|
||||||
Current = config.GetBindable<bool>(OsuSetting.PreferNoVideo)
|
Current = config.GetBindable<bool>(OsuSetting.PreferNoVideo)
|
||||||
},
|
},
|
||||||
|
new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = "Automatically download beatmaps when spectating",
|
||||||
|
Keywords = new[] { "spectator" },
|
||||||
|
Current = config.GetBindable<bool>(OsuSetting.AutomaticallyDownloadWhenSpectating),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
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.Graphics.Shapes;
|
||||||
@ -104,7 +106,7 @@ namespace osu.Game.Overlays
|
|||||||
public OverlayHeaderTabItem(T value)
|
public OverlayHeaderTabItem(T value)
|
||||||
: base(value)
|
: base(value)
|
||||||
{
|
{
|
||||||
Text.Text = value.ToString().ToLower();
|
Text.Text = ((Value as Enum)?.GetDescription() ?? Value.ToString()).ToLower();
|
||||||
Text.Font = OsuFont.GetFont(size: 14);
|
Text.Font = OsuFont.GetFont(size: 14);
|
||||||
Text.Margin = new MarginPadding { Vertical = 16.5f }; // 15px padding + 1.5px line-height difference compensation
|
Text.Margin = new MarginPadding { Vertical = 16.5f }; // 15px padding + 1.5px line-height difference compensation
|
||||||
Bar.Margin = new MarginPadding { Bottom = bar_height };
|
Bar.Margin = new MarginPadding { Bottom = bar_height };
|
||||||
|
@ -163,30 +163,27 @@ namespace osu.Game.Screens.Edit.Components.Menus
|
|||||||
|
|
||||||
protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu();
|
protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu();
|
||||||
|
|
||||||
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableSubMenuItem(item);
|
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item)
|
||||||
|
|
||||||
private class DrawableSubMenuItem : DrawableOsuMenuItem
|
|
||||||
{
|
{
|
||||||
public DrawableSubMenuItem(MenuItem item)
|
switch (item)
|
||||||
|
{
|
||||||
|
case EditorMenuItemSpacer spacer:
|
||||||
|
return new DrawableSpacer(spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.CreateDrawableMenuItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DrawableSpacer : DrawableOsuMenuItem
|
||||||
|
{
|
||||||
|
public DrawableSpacer(MenuItem item)
|
||||||
: base(item)
|
: base(item)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e) => true;
|
||||||
{
|
|
||||||
if (Item is EditorMenuItemSpacer)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return base.OnHover(e);
|
protected override bool OnClick(ClickEvent e) => true;
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
|
||||||
{
|
|
||||||
if (Item is EditorMenuItemSpacer)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return base.OnClick(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,13 +14,13 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ControlPointPart : TimelinePart<GroupVisualisation>
|
public class ControlPointPart : TimelinePart<GroupVisualisation>
|
||||||
{
|
{
|
||||||
private IBindableList<ControlPointGroup> controlPointGroups;
|
private readonly IBindableList<ControlPointGroup> controlPointGroups = new BindableList<ControlPointGroup>();
|
||||||
|
|
||||||
protected override void LoadBeatmap(WorkingBeatmap beatmap)
|
protected override void LoadBeatmap(WorkingBeatmap beatmap)
|
||||||
{
|
{
|
||||||
base.LoadBeatmap(beatmap);
|
base.LoadBeatmap(beatmap);
|
||||||
|
|
||||||
controlPointGroups = beatmap.Beatmap.ControlPointInfo.Groups.GetBoundCopy();
|
controlPointGroups.BindTo(beatmap.Beatmap.ControlPointInfo.Groups);
|
||||||
controlPointGroups.BindCollectionChanged((sender, args) =>
|
controlPointGroups.BindCollectionChanged((sender, args) =>
|
||||||
{
|
{
|
||||||
switch (args.Action)
|
switch (args.Action)
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
{
|
{
|
||||||
public readonly ControlPointGroup Group;
|
public readonly ControlPointGroup Group;
|
||||||
|
|
||||||
private BindableList<ControlPoint> controlPoints;
|
private readonly IBindableList<ControlPoint> controlPoints = new BindableList<ControlPoint>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuColour colours { get; set; }
|
private OsuColour colours { get; set; }
|
||||||
@ -30,7 +30,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
controlPoints = (BindableList<ControlPoint>)Group.ControlPoints.GetBoundCopy();
|
controlPoints.BindTo(Group.ControlPoints);
|
||||||
controlPoints.BindCollectionChanged((_, __) =>
|
controlPoints.BindCollectionChanged((_, __) =>
|
||||||
{
|
{
|
||||||
if (controlPoints.Count == 0)
|
if (controlPoints.Count == 0)
|
||||||
|
@ -7,17 +7,19 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components
|
namespace osu.Game.Screens.Edit.Compose.Components
|
||||||
{
|
{
|
||||||
public class SelectionBox : CompositeDrawable
|
public class SelectionBox : CompositeDrawable
|
||||||
{
|
{
|
||||||
public Action<float> OnRotation;
|
public Func<float, bool> OnRotation;
|
||||||
public Action<Vector2, Anchor> OnScale;
|
public Func<Vector2, Anchor, bool> OnScale;
|
||||||
public Action<Direction> OnFlip;
|
public Func<Direction, bool> OnFlip;
|
||||||
public Action OnReverse;
|
public Func<bool> OnReverse;
|
||||||
|
|
||||||
public Action OperationStarted;
|
public Action OperationStarted;
|
||||||
public Action OperationEnded;
|
public Action OperationEnded;
|
||||||
@ -105,6 +107,26 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
recreate();
|
recreate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
|
{
|
||||||
|
if (e.Repeat || !e.ControlPressed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (e.Key)
|
||||||
|
{
|
||||||
|
case Key.G:
|
||||||
|
return CanReverse && OnReverse?.Invoke() == true;
|
||||||
|
|
||||||
|
case Key.H:
|
||||||
|
return CanScaleX && OnFlip?.Invoke(Direction.Horizontal) == true;
|
||||||
|
|
||||||
|
case Key.J:
|
||||||
|
return CanScaleY && OnFlip?.Invoke(Direction.Vertical) == true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnKeyDown(e);
|
||||||
|
}
|
||||||
|
|
||||||
private void recreate()
|
private void recreate()
|
||||||
{
|
{
|
||||||
if (LoadState < LoadState.Loading)
|
if (LoadState < LoadState.Loading)
|
||||||
@ -143,7 +165,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (CanScaleX && CanScaleY) addFullScaleComponents();
|
if (CanScaleX && CanScaleY) addFullScaleComponents();
|
||||||
if (CanScaleY) addYScaleComponents();
|
if (CanScaleY) addYScaleComponents();
|
||||||
if (CanRotate) addRotationComponents();
|
if (CanRotate) addRotationComponents();
|
||||||
if (CanReverse) addButton(FontAwesome.Solid.Backward, "Reverse pattern", () => OnReverse?.Invoke());
|
if (CanReverse) addButton(FontAwesome.Solid.Backward, "Reverse pattern (Ctrl-G)", () => OnReverse?.Invoke());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addRotationComponents()
|
private void addRotationComponents()
|
||||||
@ -178,7 +200,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
private void addYScaleComponents()
|
private void addYScaleComponents()
|
||||||
{
|
{
|
||||||
addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically", () => OnFlip?.Invoke(Direction.Vertical));
|
addButton(FontAwesome.Solid.ArrowsAltV, "Flip vertically (Ctrl-J)", () => OnFlip?.Invoke(Direction.Vertical));
|
||||||
|
|
||||||
addDragHandle(Anchor.TopCentre);
|
addDragHandle(Anchor.TopCentre);
|
||||||
addDragHandle(Anchor.BottomCentre);
|
addDragHandle(Anchor.BottomCentre);
|
||||||
@ -194,7 +216,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
private void addXScaleComponents()
|
private void addXScaleComponents()
|
||||||
{
|
{
|
||||||
addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally", () => OnFlip?.Invoke(Direction.Horizontal));
|
addButton(FontAwesome.Solid.ArrowsAltH, "Flip horizontally (Ctrl-H)", () => OnFlip?.Invoke(Direction.Horizontal));
|
||||||
|
|
||||||
addDragHandle(Anchor.CentreLeft);
|
addDragHandle(Anchor.CentreLeft);
|
||||||
addDragHandle(Anchor.CentreRight);
|
addDragHandle(Anchor.CentreRight);
|
||||||
|
@ -99,10 +99,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
OperationStarted = OnOperationBegan,
|
OperationStarted = OnOperationBegan,
|
||||||
OperationEnded = OnOperationEnded,
|
OperationEnded = OnOperationEnded,
|
||||||
|
|
||||||
OnRotation = angle => HandleRotation(angle),
|
OnRotation = HandleRotation,
|
||||||
OnScale = (amount, anchor) => HandleScale(amount, anchor),
|
OnScale = HandleScale,
|
||||||
OnFlip = direction => HandleFlip(direction),
|
OnFlip = HandleFlip,
|
||||||
OnReverse = () => HandleReverse(),
|
OnReverse = HandleReverse,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Audio;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -67,8 +68,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
private TimelineControlPointDisplay controlPoints;
|
private TimelineControlPointDisplay controlPoints;
|
||||||
|
|
||||||
|
private Bindable<float> waveformOpacity;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IBindable<WorkingBeatmap> beatmap, OsuColour colours)
|
private void load(IBindable<WorkingBeatmap> beatmap, OsuColour colours, OsuConfigManager config)
|
||||||
{
|
{
|
||||||
AddRange(new Drawable[]
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
@ -95,7 +98,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
// We don't want the centre marker to scroll
|
// We don't want the centre marker to scroll
|
||||||
AddInternal(new CentreMarker { Depth = float.MaxValue });
|
AddInternal(new CentreMarker { Depth = float.MaxValue });
|
||||||
|
|
||||||
WaveformVisible.ValueChanged += visible => waveform.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint);
|
waveformOpacity = config.GetBindable<float>(OsuSetting.EditorWaveformOpacity);
|
||||||
|
waveformOpacity.BindValueChanged(_ => updateWaveformOpacity(), true);
|
||||||
|
|
||||||
|
WaveformVisible.ValueChanged += _ => updateWaveformOpacity();
|
||||||
ControlPointsVisible.ValueChanged += visible => controlPoints.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint);
|
ControlPointsVisible.ValueChanged += visible => controlPoints.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint);
|
||||||
TicksVisible.ValueChanged += visible => ticks.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint);
|
TicksVisible.ValueChanged += visible => ticks.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint);
|
||||||
|
|
||||||
@ -115,6 +121,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateWaveformOpacity() =>
|
||||||
|
waveform.FadeTo(WaveformVisible.Value ? waveformOpacity.Value : 0, 200, Easing.OutQuint);
|
||||||
|
|
||||||
private float getZoomLevelForVisibleMilliseconds(double milliseconds) => Math.Max(1, (float)(track.Length / milliseconds));
|
private float getZoomLevelForVisibleMilliseconds(double milliseconds) => Math.Max(1, (float)(track.Length / milliseconds));
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -165,6 +174,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
if (!track.IsLoaded || track.Length == 0)
|
if (!track.IsLoaded || track.Length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// covers the case where the user starts playback after a drag is in progress.
|
||||||
|
// we want to ensure the clock is always stopped during drags to avoid weird audio playback.
|
||||||
|
if (handlingDragInput)
|
||||||
|
editorClock.Stop();
|
||||||
|
|
||||||
ScrollTo((float)(editorClock.CurrentTime / track.Length) * Content.DrawWidth, false);
|
ScrollTo((float)(editorClock.CurrentTime / track.Length) * Content.DrawWidth, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class TimelineControlPointDisplay : TimelinePart<TimelineControlPointGroup>
|
public class TimelineControlPointDisplay : TimelinePart<TimelineControlPointGroup>
|
||||||
{
|
{
|
||||||
private IBindableList<ControlPointGroup> controlPointGroups;
|
private readonly IBindableList<ControlPointGroup> controlPointGroups = new BindableList<ControlPointGroup>();
|
||||||
|
|
||||||
public TimelineControlPointDisplay()
|
public TimelineControlPointDisplay()
|
||||||
{
|
{
|
||||||
@ -27,7 +27,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
{
|
{
|
||||||
base.LoadBeatmap(beatmap);
|
base.LoadBeatmap(beatmap);
|
||||||
|
|
||||||
controlPointGroups = beatmap.Beatmap.ControlPointInfo.Groups.GetBoundCopy();
|
controlPointGroups.BindTo(beatmap.Beatmap.ControlPointInfo.Groups);
|
||||||
controlPointGroups.BindCollectionChanged((sender, args) =>
|
controlPointGroups.BindCollectionChanged((sender, args) =>
|
||||||
{
|
{
|
||||||
switch (args.Action)
|
switch (args.Action)
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
{
|
{
|
||||||
public readonly ControlPointGroup Group;
|
public readonly ControlPointGroup Group;
|
||||||
|
|
||||||
private BindableList<ControlPoint> controlPoints;
|
private readonly IBindableList<ControlPoint> controlPoints = new BindableList<ControlPoint>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuColour colours { get; set; }
|
private OsuColour colours { get; set; }
|
||||||
@ -34,7 +34,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
controlPoints = (BindableList<ControlPoint>)Group.ControlPoints.GetBoundCopy();
|
controlPoints.BindTo(Group.ControlPoints);
|
||||||
controlPoints.BindCollectionChanged((_, __) =>
|
controlPoints.BindCollectionChanged((_, __) =>
|
||||||
{
|
{
|
||||||
ClearInternal();
|
ClearInternal();
|
||||||
|
@ -113,21 +113,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
protected override bool OnScroll(ScrollEvent e)
|
protected override bool OnScroll(ScrollEvent e)
|
||||||
{
|
{
|
||||||
if (e.IsPrecise)
|
if (e.AltPressed)
|
||||||
{
|
{
|
||||||
|
// zoom when holding alt.
|
||||||
|
setZoomTarget(zoomTarget + e.ScrollDelta.Y, zoomedContent.ToLocalSpace(e.ScreenSpaceMousePosition).X);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// can't handle scroll correctly while playing.
|
// can't handle scroll correctly while playing.
|
||||||
// the editor will handle this case for us.
|
// the editor will handle this case for us.
|
||||||
if (editorClock?.IsRunning == true)
|
if (editorClock?.IsRunning == true)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// for now, we don't support zoom when using a precision scroll device. this needs gesture support.
|
|
||||||
return base.OnScroll(e);
|
return base.OnScroll(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
setZoomTarget(zoomTarget + e.ScrollDelta.Y, zoomedContent.ToLocalSpace(e.ScreenSpaceMousePosition).X);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateZoomedContentWidth() => zoomedContent.Width = DrawWidth * currentZoom;
|
private void updateZoomedContentWidth() => zoomedContent.Width = DrawWidth * currentZoom;
|
||||||
|
|
||||||
private float zoomTarget = 1;
|
private float zoomTarget = 1;
|
||||||
|
@ -20,6 +20,7 @@ using osu.Framework.Platform;
|
|||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Cursor;
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -103,7 +104,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
private MusicController music { get; set; }
|
private MusicController music { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, GameHost host)
|
private void load(OsuColour colours, GameHost host, OsuConfigManager config)
|
||||||
{
|
{
|
||||||
beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor;
|
beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor;
|
||||||
beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue);
|
beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue);
|
||||||
@ -208,6 +209,13 @@ namespace osu.Game.Screens.Edit
|
|||||||
copyMenuItem = new EditorMenuItem("Copy", MenuItemType.Standard, Copy),
|
copyMenuItem = new EditorMenuItem("Copy", MenuItemType.Standard, Copy),
|
||||||
pasteMenuItem = new EditorMenuItem("Paste", MenuItemType.Standard, Paste),
|
pasteMenuItem = new EditorMenuItem("Paste", MenuItemType.Standard, Paste),
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
new MenuItem("View")
|
||||||
|
{
|
||||||
|
Items = new[]
|
||||||
|
{
|
||||||
|
new WaveformOpacityMenu(config)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
|
|
||||||
private class ControlGroupAttributes : CompositeDrawable
|
private class ControlGroupAttributes : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly IBindableList<ControlPoint> controlPoints;
|
private readonly IBindableList<ControlPoint> controlPoints = new BindableList<ControlPoint>();
|
||||||
|
|
||||||
private readonly FillFlowContainer fill;
|
private readonly FillFlowContainer fill;
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
Spacing = new Vector2(2)
|
Spacing = new Vector2(2)
|
||||||
};
|
};
|
||||||
|
|
||||||
controlPoints = group.ControlPoints.GetBoundCopy();
|
controlPoints.BindTo(group.ControlPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
private OsuButton deleteButton;
|
private OsuButton deleteButton;
|
||||||
private ControlPointTable table;
|
private ControlPointTable table;
|
||||||
|
|
||||||
private IBindableList<ControlPointGroup> controlGroups;
|
private readonly IBindableList<ControlPointGroup> controlPointGroups = new BindableList<ControlPointGroup>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private EditorClock clock { get; set; }
|
private EditorClock clock { get; set; }
|
||||||
@ -124,11 +124,10 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
|
|
||||||
selectedGroup.BindValueChanged(selected => { deleteButton.Enabled.Value = selected.NewValue != null; }, true);
|
selectedGroup.BindValueChanged(selected => { deleteButton.Enabled.Value = selected.NewValue != null; }, true);
|
||||||
|
|
||||||
controlGroups = Beatmap.Value.Beatmap.ControlPointInfo.Groups.GetBoundCopy();
|
controlPointGroups.BindTo(Beatmap.Value.Beatmap.ControlPointInfo.Groups);
|
||||||
|
controlPointGroups.BindCollectionChanged((sender, args) =>
|
||||||
controlGroups.BindCollectionChanged((sender, args) =>
|
|
||||||
{
|
{
|
||||||
table.ControlGroups = controlGroups;
|
table.ControlGroups = controlPointGroups;
|
||||||
changeHandler.SaveState();
|
changeHandler.SaveState();
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
46
osu.Game/Screens/Edit/WaveformOpacityMenu.cs
Normal file
46
osu.Game/Screens/Edit/WaveformOpacityMenu.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
internal class WaveformOpacityMenu : MenuItem
|
||||||
|
{
|
||||||
|
private readonly Bindable<float> waveformOpacity;
|
||||||
|
|
||||||
|
private readonly Dictionary<float, ToggleMenuItem> menuItemLookup = new Dictionary<float, ToggleMenuItem>();
|
||||||
|
|
||||||
|
public WaveformOpacityMenu(OsuConfigManager config)
|
||||||
|
: base("Waveform opacity")
|
||||||
|
{
|
||||||
|
Items = new[]
|
||||||
|
{
|
||||||
|
createMenuItem(0.25f),
|
||||||
|
createMenuItem(0.5f),
|
||||||
|
createMenuItem(0.75f),
|
||||||
|
createMenuItem(1f),
|
||||||
|
};
|
||||||
|
|
||||||
|
waveformOpacity = config.GetBindable<float>(OsuSetting.EditorWaveformOpacity);
|
||||||
|
waveformOpacity.BindValueChanged(opacity =>
|
||||||
|
{
|
||||||
|
foreach (var kvp in menuItemLookup)
|
||||||
|
kvp.Value.State.Value = kvp.Key == opacity.NewValue;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ToggleMenuItem createMenuItem(float opacity)
|
||||||
|
{
|
||||||
|
var item = new ToggleMenuItem($"{opacity * 100}%", MenuItemType.Standard, _ => updateOpacity(opacity));
|
||||||
|
menuItemLookup[opacity] = item;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateOpacity(float opacity) => waveformOpacity.Value = opacity;
|
||||||
|
}
|
||||||
|
}
|
@ -9,20 +9,26 @@ 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.Graphics.Sprites;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
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.Online.Spectator;
|
using osu.Game.Online.Spectator;
|
||||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.Replays.Types;
|
using osu.Game.Rulesets.Replays.Types;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Multi.Match.Components;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -63,6 +69,12 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
|
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
|
||||||
|
|
||||||
|
private TriangleButton watchButton;
|
||||||
|
|
||||||
|
private SettingsCheckbox automaticDownload;
|
||||||
|
|
||||||
|
private BeatmapSetInfo onlineBeatmap;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Becomes true if a new state is waiting to be loaded (while this screen was not active).
|
/// Becomes true if a new state is waiting to be loaded (while this screen was not active).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -74,12 +86,27 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OsuColour colours, OsuConfigManager config)
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChild = new Container
|
||||||
{
|
{
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 20,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
AutoSizeDuration = 500,
|
||||||
|
AutoSizeEasing = Easing.OutQuint,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colours.GreySeafoamDark,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
|
Margin = new MarginPadding(20),
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -89,32 +116,61 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Text = "Currently spectating",
|
Text = "Spectator Mode",
|
||||||
Font = OsuFont.Default.With(size: 30),
|
Font = OsuFont.Default.With(size: 30),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Spacing = new Vector2(15),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
new UserGridPanel(targetUser)
|
new UserGridPanel(targetUser)
|
||||||
{
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Height = 145,
|
||||||
Width = 290,
|
Width = 290,
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
},
|
},
|
||||||
new OsuSpriteText
|
new SpriteIcon
|
||||||
{
|
{
|
||||||
Text = "playing",
|
Size = new Vector2(40),
|
||||||
Font = OsuFont.Default.With(size: 30),
|
Icon = FontAwesome.Solid.ArrowRight,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.CentreLeft,
|
||||||
},
|
},
|
||||||
beatmapPanelContainer = new Container
|
beatmapPanelContainer = new Container
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.CentreLeft,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
automaticDownload = new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = "Automatically download beatmaps",
|
||||||
|
Current = config.GetBindable<bool>(OsuSetting.AutomaticallyDownloadWhenSpectating),
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
},
|
||||||
|
watchButton = new PurpleTriangleButton
|
||||||
|
{
|
||||||
|
Text = "Start Watching",
|
||||||
|
Width = 250,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Action = attemptStart,
|
||||||
|
Enabled = { Value = false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,12 +186,14 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
managerUpdated = beatmaps.ItemUpdated.GetBoundCopy();
|
managerUpdated = beatmaps.ItemUpdated.GetBoundCopy();
|
||||||
managerUpdated.BindValueChanged(beatmapUpdated);
|
managerUpdated.BindValueChanged(beatmapUpdated);
|
||||||
|
|
||||||
|
automaticDownload.Current.BindValueChanged(_ => checkForAutomaticDownload());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> beatmap)
|
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> beatmap)
|
||||||
{
|
{
|
||||||
if (beatmap.NewValue.TryGetTarget(out var beatmapSet) && beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID == state.BeatmapID))
|
if (beatmap.NewValue.TryGetTarget(out var beatmapSet) && beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID == state.BeatmapID))
|
||||||
attemptStart();
|
Schedule(attemptStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void userSentFrames(int userId, FrameDataBundle data)
|
private void userSentFrames(int userId, FrameDataBundle data)
|
||||||
@ -189,14 +247,26 @@ namespace osu.Game.Screens.Play
|
|||||||
if (userId != targetUser.Id)
|
if (userId != targetUser.Id)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (replay == null) return;
|
if (replay != null)
|
||||||
|
{
|
||||||
replay.HasReceivedAllFrames = true;
|
replay.HasReceivedAllFrames = true;
|
||||||
replay = null;
|
replay = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Schedule(clearDisplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearDisplay()
|
||||||
|
{
|
||||||
|
watchButton.Enabled.Value = false;
|
||||||
|
beatmapPanelContainer.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
private void attemptStart()
|
private void attemptStart()
|
||||||
{
|
{
|
||||||
|
clearDisplay();
|
||||||
|
showBeatmapPanel(state);
|
||||||
|
|
||||||
var resolvedRuleset = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == state.RulesetID)?.CreateInstance();
|
var resolvedRuleset = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == state.RulesetID)?.CreateInstance();
|
||||||
|
|
||||||
// ruleset not available
|
// ruleset not available
|
||||||
@ -210,7 +280,6 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
if (resolvedBeatmap == null)
|
if (resolvedBeatmap == null)
|
||||||
{
|
{
|
||||||
showBeatmapPanel(state.BeatmapID.Value);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,6 +297,7 @@ namespace osu.Game.Screens.Play
|
|||||||
rulesetInstance = resolvedRuleset;
|
rulesetInstance = resolvedRuleset;
|
||||||
|
|
||||||
beatmap.Value = beatmaps.GetWorkingBeatmap(resolvedBeatmap);
|
beatmap.Value = beatmaps.GetWorkingBeatmap(resolvedBeatmap);
|
||||||
|
watchButton.Enabled.Value = true;
|
||||||
|
|
||||||
this.Push(new SpectatorPlayerLoader(new Score
|
this.Push(new SpectatorPlayerLoader(new Score
|
||||||
{
|
{
|
||||||
@ -236,17 +306,43 @@ namespace osu.Game.Screens.Play
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showBeatmapPanel(int beatmapId)
|
private void showBeatmapPanel(SpectatorState state)
|
||||||
{
|
{
|
||||||
var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId);
|
if (state?.BeatmapID == null)
|
||||||
|
{
|
||||||
|
beatmapPanelContainer.Clear();
|
||||||
|
onlineBeatmap = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var req = new GetBeatmapSetRequest(state.BeatmapID.Value, BeatmapSetLookupType.BeatmapId);
|
||||||
req.Success += res => Schedule(() =>
|
req.Success += res => Schedule(() =>
|
||||||
{
|
{
|
||||||
beatmapPanelContainer.Child = new GridBeatmapPanel(res.ToBeatmapSet(rulesets));
|
if (state != this.state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
onlineBeatmap = res.ToBeatmapSet(rulesets);
|
||||||
|
beatmapPanelContainer.Child = new GridBeatmapPanel(onlineBeatmap);
|
||||||
|
checkForAutomaticDownload();
|
||||||
});
|
});
|
||||||
|
|
||||||
api.Queue(req);
|
api.Queue(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkForAutomaticDownload()
|
||||||
|
{
|
||||||
|
if (onlineBeatmap == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!automaticDownload.Current.Value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (beatmaps.IsAvailableLocally(onlineBeatmap))
|
||||||
|
return;
|
||||||
|
|
||||||
|
beatmaps.Download(onlineBeatmap);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
@ -3,33 +3,60 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Online.Spectator;
|
using osu.Game.Online.Spectator;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Ranking;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
public class SpectatorPlayer : ReplayPlayer
|
public class SpectatorPlayer : Player
|
||||||
{
|
{
|
||||||
[Resolved]
|
private readonly Score score;
|
||||||
private SpectatorStreamingClient spectatorStreaming { get; set; }
|
|
||||||
|
protected override bool CheckModsAllowFailure() => false; // todo: better support starting mid-way through beatmap
|
||||||
|
|
||||||
public SpectatorPlayer(Score score)
|
public SpectatorPlayer(Score score)
|
||||||
: base(score)
|
|
||||||
{
|
{
|
||||||
|
this.score = score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ResultsScreen CreateResults(ScoreInfo score)
|
||||||
|
{
|
||||||
|
return new SpectatorResultsScreen(score);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private SpectatorStreamingClient spectatorStreaming { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
spectatorStreaming.OnUserBeganPlaying += userBeganPlaying;
|
spectatorStreaming.OnUserBeganPlaying += userBeganPlaying;
|
||||||
|
|
||||||
|
AddInternal(new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = $"Watching {score.ScoreInfo.User.Username} playing live!",
|
||||||
|
Font = OsuFont.Default.With(size: 30),
|
||||||
|
Y = 100,
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PrepareReplay()
|
||||||
|
{
|
||||||
|
DrawableRuleset?.SetReplayScore(score);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart)
|
protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart)
|
||||||
{
|
{
|
||||||
// if we already have frames, start gameplay at the point in time they exist, should they be too far into the beatmap.
|
// if we already have frames, start gameplay at the point in time they exist, should they be too far into the beatmap.
|
||||||
double? firstFrameTime = Score.Replay.Frames.FirstOrDefault()?.Time;
|
double? firstFrameTime = score.Replay.Frames.FirstOrDefault()?.Time;
|
||||||
|
|
||||||
if (firstFrameTime == null || firstFrameTime <= gameplayStart + 5000)
|
if (firstFrameTime == null || firstFrameTime <= gameplayStart + 5000)
|
||||||
return base.CreateGameplayClockContainer(beatmap, gameplayStart);
|
return base.CreateGameplayClockContainer(beatmap, gameplayStart);
|
||||||
@ -43,6 +70,16 @@ namespace osu.Game.Screens.Play
|
|||||||
return base.OnExiting(next);
|
return base.OnExiting(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void userBeganPlaying(int userId, SpectatorState state)
|
||||||
|
{
|
||||||
|
if (userId != score.ScoreInfo.UserID) return;
|
||||||
|
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
if (this.IsCurrentScreen()) this.Exit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
@ -50,11 +87,5 @@ namespace osu.Game.Screens.Play
|
|||||||
if (spectatorStreaming != null)
|
if (spectatorStreaming != null)
|
||||||
spectatorStreaming.OnUserBeganPlaying -= userBeganPlaying;
|
spectatorStreaming.OnUserBeganPlaying -= userBeganPlaying;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void userBeganPlaying(int userId, SpectatorState state)
|
|
||||||
{
|
|
||||||
if (userId == Score.ScoreInfo.UserID)
|
|
||||||
Schedule(this.Exit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
47
osu.Game/Screens/Play/SpectatorResultsScreen.cs
Normal file
47
osu.Game/Screens/Play/SpectatorResultsScreen.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// 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.Screens;
|
||||||
|
using osu.Game.Online.Spectator;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Ranking;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play
|
||||||
|
{
|
||||||
|
public class SpectatorResultsScreen : SoloResultsScreen
|
||||||
|
{
|
||||||
|
public SpectatorResultsScreen(ScoreInfo score)
|
||||||
|
: base(score)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private SpectatorStreamingClient spectatorStreaming { get; set; }
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
spectatorStreaming.OnUserBeganPlaying += userBeganPlaying;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void userBeganPlaying(int userId, SpectatorState state)
|
||||||
|
{
|
||||||
|
if (userId == Score.UserID)
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
if (this.IsCurrentScreen()) this.Exit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (spectatorStreaming != null)
|
||||||
|
spectatorStreaming.OnUserBeganPlaying -= userBeganPlaying;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -85,6 +86,8 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private WorkingBeatmap beatmap;
|
private WorkingBeatmap beatmap;
|
||||||
|
|
||||||
|
private CancellationTokenSource cancellationSource;
|
||||||
|
|
||||||
public WorkingBeatmap Beatmap
|
public WorkingBeatmap Beatmap
|
||||||
{
|
{
|
||||||
get => beatmap;
|
get => beatmap;
|
||||||
@ -93,9 +96,11 @@ namespace osu.Game.Screens.Select
|
|||||||
if (beatmap == value) return;
|
if (beatmap == value) return;
|
||||||
|
|
||||||
beatmap = value;
|
beatmap = value;
|
||||||
|
cancellationSource?.Cancel();
|
||||||
|
cancellationSource = new CancellationTokenSource();
|
||||||
|
|
||||||
beatmapDifficulty?.UnbindAll();
|
beatmapDifficulty?.UnbindAll();
|
||||||
beatmapDifficulty = difficultyManager.GetBindableDifficulty(beatmap.BeatmapInfo);
|
beatmapDifficulty = difficultyManager.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token);
|
||||||
beatmapDifficulty.BindValueChanged(_ => updateDisplay());
|
beatmapDifficulty.BindValueChanged(_ => updateDisplay());
|
||||||
|
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
@ -107,6 +112,10 @@ namespace osu.Game.Screens.Select
|
|||||||
private BufferedWedgeInfo loadingInfo;
|
private BufferedWedgeInfo loadingInfo;
|
||||||
|
|
||||||
private void updateDisplay()
|
private void updateDisplay()
|
||||||
|
{
|
||||||
|
Scheduler.AddOnce(perform);
|
||||||
|
|
||||||
|
void perform()
|
||||||
{
|
{
|
||||||
void removeOldInfo()
|
void removeOldInfo()
|
||||||
{
|
{
|
||||||
@ -136,6 +145,13 @@ namespace osu.Game.Screens.Select
|
|||||||
Add(Info = loaded);
|
Add(Info = loaded);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
cancellationSource?.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
public class BufferedWedgeInfo : BufferedContainer
|
public class BufferedWedgeInfo : BufferedContainer
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,7 @@ using osu.Framework.Input.Bindings;
|
|||||||
using osu.Game.Collections;
|
using osu.Game.Collections;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select
|
namespace osu.Game.Screens.Select
|
||||||
{
|
{
|
||||||
@ -519,7 +520,7 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
ModSelect.SelectedMods.BindTo(selectedMods);
|
ModSelect.SelectedMods.BindTo(selectedMods);
|
||||||
|
|
||||||
music.TrackChanged += ensureTrackLooping;
|
beginLooping();
|
||||||
}
|
}
|
||||||
|
|
||||||
private const double logo_transition = 250;
|
private const double logo_transition = 250;
|
||||||
@ -570,8 +571,7 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
BeatmapDetails.Refresh();
|
BeatmapDetails.Refresh();
|
||||||
|
|
||||||
music.CurrentTrack.Looping = true;
|
beginLooping();
|
||||||
music.TrackChanged += ensureTrackLooping;
|
|
||||||
music.ResetTrackAdjustments();
|
music.ResetTrackAdjustments();
|
||||||
|
|
||||||
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
|
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
|
||||||
@ -597,8 +597,7 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
BeatmapOptions.Hide();
|
BeatmapOptions.Hide();
|
||||||
|
|
||||||
music.CurrentTrack.Looping = false;
|
endLooping();
|
||||||
music.TrackChanged -= ensureTrackLooping;
|
|
||||||
|
|
||||||
this.ScaleTo(1.1f, 250, Easing.InSine);
|
this.ScaleTo(1.1f, 250, Easing.InSine);
|
||||||
|
|
||||||
@ -619,12 +618,33 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
FilterControl.Deactivate();
|
FilterControl.Deactivate();
|
||||||
|
|
||||||
music.CurrentTrack.Looping = false;
|
endLooping();
|
||||||
music.TrackChanged -= ensureTrackLooping;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool isHandlingLooping;
|
||||||
|
|
||||||
|
private void beginLooping()
|
||||||
|
{
|
||||||
|
Debug.Assert(!isHandlingLooping);
|
||||||
|
|
||||||
|
music.CurrentTrack.Looping = isHandlingLooping = true;
|
||||||
|
|
||||||
|
music.TrackChanged += ensureTrackLooping;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void endLooping()
|
||||||
|
{
|
||||||
|
// may be called multiple times during screen exit process.
|
||||||
|
if (!isHandlingLooping)
|
||||||
|
return;
|
||||||
|
|
||||||
|
music.CurrentTrack.Looping = isHandlingLooping = false;
|
||||||
|
|
||||||
|
music.TrackChanged -= ensureTrackLooping;
|
||||||
|
}
|
||||||
|
|
||||||
private void ensureTrackLooping(WorkingBeatmap beatmap, TrackChangeDirection changeDirection)
|
private void ensureTrackLooping(WorkingBeatmap beatmap, TrackChangeDirection changeDirection)
|
||||||
=> music.CurrentTrack.Looping = true;
|
=> music.CurrentTrack.Looping = true;
|
||||||
|
|
||||||
|
@ -20,6 +20,10 @@ namespace osu.Game.Users
|
|||||||
{
|
{
|
||||||
public readonly User User;
|
public readonly User User;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform an action in addition to showing the user's profile.
|
||||||
|
/// This should be used to perform auxiliary tasks and not as a primary action for clicking a user panel (to maintain a consistent UX).
|
||||||
|
/// </summary>
|
||||||
public new Action Action;
|
public new Action Action;
|
||||||
|
|
||||||
protected Action ViewProfile { get; private set; }
|
protected Action ViewProfile { get; private set; }
|
||||||
|
@ -73,6 +73,14 @@
|
|||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.1029.1" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.1029.1" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1030.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.1030.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);NU1605</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.0.3" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<PackageReference Include="DiffPlex" Version="1.6.3" />
|
<PackageReference Include="DiffPlex" Version="1.6.3" />
|
||||||
|
Loading…
Reference in New Issue
Block a user