mirror of
https://github.com/ppy/osu.git
synced 2024-12-16 21:42:55 +08:00
Merge branch 'master' into custom-ipc-location
This commit is contained in:
commit
6e8a964c45
@ -15,6 +15,8 @@ Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the
|
||||
|
||||
This project is under heavy development, but is in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
|
||||
|
||||
**IMPORTANT:** Gameplay mechanics (and other features which you may have come to know and love) are in a constant state of flux. Game balance and final quality-of-life passses come at the end of development, preceeded by experimentation and changes which may potentially **reduce playability or usability**. This is done in order to allow us to move forward as developers and designers more efficiently. If this offends you, please consider sticking to the stable releases of osu! (found on the website). We are not yet open to heated discussion over game mechanics and will not be using github as a forum for such discussions just yet.
|
||||
|
||||
We are accepting bug reports (please report with as much detail as possible and follow the existing issue templates). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
|
||||
|
||||
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
|
||||
|
@ -52,6 +52,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.512.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.519.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.528.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -8,7 +8,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Resources;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Benchmarks
|
||||
{
|
||||
@ -18,8 +18,8 @@ namespace osu.Game.Benchmarks
|
||||
|
||||
public override void SetUp()
|
||||
{
|
||||
using (var resources = new DllResourceStore(OsuResources.ResourceAssembly))
|
||||
using (var archive = resources.GetStream("Beatmaps/241526 Soleily - Renatus.osz"))
|
||||
using (var resources = new DllResourceStore(typeof(TestResources).Assembly))
|
||||
using (var archive = resources.GetStream("Resources/Archives/241526 Soleily - Renatus.osz"))
|
||||
using (var reader = new ZipArchiveReader(archive))
|
||||
reader.GetStream("Soleily - Renatus (Gamu) [Insane].osu").CopyTo(beatmapStream);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu.Game.Tests\osu.Game.Tests.csproj" />
|
||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Threading;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
@ -14,13 +15,13 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
||||
|
||||
protected override void CreateNestedHitObjects()
|
||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||
{
|
||||
base.CreateNestedHitObjects();
|
||||
createBananas();
|
||||
base.CreateNestedHitObjects(cancellationToken);
|
||||
createBananas(cancellationToken);
|
||||
}
|
||||
|
||||
private void createBananas()
|
||||
private void createBananas(CancellationToken cancellationToken)
|
||||
{
|
||||
double spacing = Duration;
|
||||
while (spacing > 100)
|
||||
@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
for (double i = StartTime; i <= EndTime; i += spacing)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
AddNested(new Banana
|
||||
{
|
||||
Samples = Samples,
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@ -45,9 +46,9 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
||||
}
|
||||
|
||||
protected override void CreateNestedHitObjects()
|
||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||
{
|
||||
base.CreateNestedHitObjects();
|
||||
base.CreateNestedHitObjects(cancellationToken);
|
||||
|
||||
var dropletSamples = Samples.Select(s => new HitSampleInfo
|
||||
{
|
||||
@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
SliderEventDescriptor? lastEvent = null;
|
||||
|
||||
foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset))
|
||||
foreach (var e in SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset, cancellationToken))
|
||||
{
|
||||
// generate tiny droplets since the last point
|
||||
if (lastEvent != null)
|
||||
@ -73,6 +74,8 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
for (double t = timeBetweenTiny; t < sinceLastTick; t += timeBetweenTiny)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
AddNested(new TinyDroplet
|
||||
{
|
||||
StartTime = t + lastEvent.Value.Time,
|
||||
|
@ -8,7 +8,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Edit;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -19,8 +18,7 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[Cached(Type = typeof(IManiaHitObjectComposer))]
|
||||
public abstract class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene, IManiaHitObjectComposer
|
||||
public abstract class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene
|
||||
{
|
||||
private readonly Column column;
|
||||
|
||||
@ -48,7 +46,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
var time = column.TimeAtScreenSpacePosition(InputManager.CurrentState.Mouse.Position);
|
||||
var pos = column.ScreenSpacePositionAtTime(time);
|
||||
|
||||
return new ManiaSnapResult(pos, time, column);
|
||||
return new SnapResult(pos, time, column);
|
||||
}
|
||||
|
||||
protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both };
|
||||
|
@ -4,15 +4,13 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Mania.Edit;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[Cached(Type = typeof(IManiaHitObjectComposer))]
|
||||
public abstract class ManiaSelectionBlueprintTestScene : SelectionBlueprintTestScene, IManiaHitObjectComposer
|
||||
public abstract class ManiaSelectionBlueprintTestScene : SelectionBlueprintTestScene
|
||||
{
|
||||
[Cached(Type = typeof(IAdjustableClock))]
|
||||
private readonly IAdjustableClock clock = new StopwatchClock();
|
||||
|
@ -2,23 +2,27 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Edit;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[Cached(typeof(IManiaHitObjectComposer))]
|
||||
public class TestSceneManiaBeatSnapGrid : EditorClockTestScene, IManiaHitObjectComposer
|
||||
public class TestSceneManiaBeatSnapGrid : EditorClockTestScene
|
||||
{
|
||||
[Cached(typeof(IScrollingInfo))]
|
||||
private ScrollingTestContainer.TestScrollingInfo scrollingInfo = new ScrollingTestContainer.TestScrollingInfo();
|
||||
@ -50,7 +54,10 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
Clock = new FramedClock(new StopwatchClock())
|
||||
},
|
||||
beatSnapGrid = new ManiaBeatSnapGrid()
|
||||
new TestHitObjectComposer(Playfield)
|
||||
{
|
||||
Child = beatSnapGrid = new ManiaBeatSnapGrid()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -67,4 +74,51 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
public ManiaPlayfield Playfield { get; }
|
||||
}
|
||||
|
||||
public class TestHitObjectComposer : HitObjectComposer
|
||||
{
|
||||
public override Playfield Playfield { get; }
|
||||
public override IEnumerable<DrawableHitObject> HitObjects => Enumerable.Empty<DrawableHitObject>();
|
||||
public override bool CursorInPlacementArea => false;
|
||||
|
||||
public TestHitObjectComposer(Playfield playfield)
|
||||
{
|
||||
Playfield = playfield;
|
||||
}
|
||||
|
||||
public Drawable Child
|
||||
{
|
||||
set => InternalChild = value;
|
||||
}
|
||||
|
||||
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override float GetBeatSnapDistanceAt(double referenceTime)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override float DurationToDistance(double referenceTime, double duration)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override double DistanceToDuration(double referenceTime, float distance)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -20,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
private readonly EditNotePiece tailPiece;
|
||||
|
||||
[Resolved]
|
||||
private IManiaHitObjectComposer composer { get; set; }
|
||||
private IScrollingInfo scrollingInfo { get; set; }
|
||||
|
||||
public HoldNotePlacementBlueprint()
|
||||
: base(new HoldNote())
|
||||
@ -43,6 +44,19 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
{
|
||||
headPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.StartTime)).Y;
|
||||
tailPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.EndTime)).Y;
|
||||
|
||||
switch (scrollingInfo.Direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Down:
|
||||
headPiece.Y -= headPiece.DrawHeight / 2;
|
||||
tailPiece.Y -= tailPiece.DrawHeight / 2;
|
||||
break;
|
||||
|
||||
case ScrollingDirection.Up:
|
||||
headPiece.Y += headPiece.DrawHeight / 2;
|
||||
tailPiece.Y += tailPiece.DrawHeight / 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var topPosition = new Vector2(headPiece.DrawPosition.X, Math.Min(headPiece.DrawPosition.Y, tailPiece.DrawPosition.Y));
|
||||
@ -78,9 +92,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result is ManiaSnapResult maniaResult)
|
||||
if (result.Playfield != null)
|
||||
{
|
||||
headPiece.Width = tailPiece.Width = maniaResult.Column.DrawWidth;
|
||||
headPiece.Width = tailPiece.Width = result.Playfield.DrawWidth;
|
||||
headPiece.X = tailPiece.X = ToLocalSpace(result.ScreenSpacePosition).X;
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
base.UpdatePosition(result);
|
||||
|
||||
if (!PlacementActive)
|
||||
Column = (result as ManiaSnapResult)?.Column;
|
||||
Column = result.Playfield as Column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,17 +11,14 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
{
|
||||
public class ManiaSelectionBlueprint : OverlaySelectionBlueprint
|
||||
public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint
|
||||
{
|
||||
public new DrawableManiaHitObject DrawableObject => (DrawableManiaHitObject)base.DrawableObject;
|
||||
|
||||
[Resolved]
|
||||
private IScrollingInfo scrollingInfo { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IManiaHitObjectComposer composer { get; set; }
|
||||
|
||||
public ManiaSelectionBlueprint(DrawableHitObject drawableObject)
|
||||
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
|
||||
: base(drawableObject)
|
||||
{
|
||||
RelativeSizeAxes = Axes.None;
|
||||
|
@ -26,9 +26,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||
{
|
||||
base.UpdatePosition(result);
|
||||
|
||||
if (result is ManiaSnapResult maniaResult)
|
||||
if (result.Playfield != null)
|
||||
{
|
||||
piece.Width = maniaResult.Column.DrawWidth;
|
||||
piece.Width = result.Playfield.DrawWidth;
|
||||
piece.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +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.Game.Rulesets.Mania.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public interface IManiaHitObjectComposer
|
||||
{
|
||||
ManiaPlayfield Playfield { get; }
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
@ -63,9 +65,9 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
private (double start, double end)? selectionTimeRange;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IManiaHitObjectComposer composer)
|
||||
private void load(HitObjectComposer composer)
|
||||
{
|
||||
foreach (var stage in composer.Playfield.Stages)
|
||||
foreach (var stage in ((ManiaPlayfield)composer.Playfield).Stages)
|
||||
{
|
||||
foreach (var column in stage.Columns)
|
||||
{
|
||||
|
@ -9,6 +9,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -19,8 +20,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
[Cached(Type = typeof(IManiaHitObjectComposer))]
|
||||
public class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>, IManiaHitObjectComposer
|
||||
public class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>
|
||||
{
|
||||
private DrawableManiaEditRuleset drawableRuleset;
|
||||
private ManiaBeatSnapGrid beatSnapGrid;
|
||||
@ -49,28 +49,35 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
|
||||
public ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield);
|
||||
public new ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield);
|
||||
|
||||
public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo;
|
||||
|
||||
protected override Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) =>
|
||||
Playfield.GetColumnByPosition(screenSpacePosition);
|
||||
|
||||
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||
{
|
||||
var column = Playfield.GetColumnByPosition(screenSpacePosition);
|
||||
var result = base.SnapScreenSpacePositionToValidTime(screenSpacePosition);
|
||||
|
||||
if (column == null)
|
||||
return new SnapResult(screenSpacePosition, null);
|
||||
switch (ScrollingInfo.Direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Down:
|
||||
result.ScreenSpacePosition -= new Vector2(0, getNoteHeight() / 2);
|
||||
break;
|
||||
|
||||
double targetTime = column.TimeAtScreenSpacePosition(screenSpacePosition);
|
||||
case ScrollingDirection.Up:
|
||||
result.ScreenSpacePosition += new Vector2(0, getNoteHeight() / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
// apply beat snapping
|
||||
targetTime = BeatSnapProvider.SnapTime(targetTime);
|
||||
|
||||
// convert back to screen space
|
||||
screenSpacePosition = column.ScreenSpacePositionAtTime(targetTime);
|
||||
|
||||
return new ManiaSnapResult(screenSpacePosition, targetTime, column);
|
||||
return result;
|
||||
}
|
||||
|
||||
private float getNoteHeight() =>
|
||||
Playfield.GetColumn(0).ToScreenSpace(new Vector2(DefaultNotePiece.NOTE_HEIGHT)).Y -
|
||||
Playfield.GetColumn(0).ToScreenSpace(Vector2.Zero).Y;
|
||||
|
||||
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||
{
|
||||
drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
private IScrollingInfo scrollingInfo { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IManiaHitObjectComposer composer { get; set; }
|
||||
private HitObjectComposer composer { get; set; }
|
||||
|
||||
public override bool HandleMovement(MoveSelectionEvent moveEvent)
|
||||
{
|
||||
@ -31,7 +32,9 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
|
||||
private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent)
|
||||
{
|
||||
var currentColumn = composer.Playfield.GetColumnByPosition(moveEvent.ScreenSpacePosition);
|
||||
var maniaPlayfield = ((ManiaHitObjectComposer)composer).Playfield;
|
||||
|
||||
var currentColumn = maniaPlayfield.GetColumnByPosition(moveEvent.ScreenSpacePosition);
|
||||
if (currentColumn == null)
|
||||
return;
|
||||
|
||||
@ -50,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
||||
maxColumn = obj.Column;
|
||||
}
|
||||
|
||||
columnDelta = Math.Clamp(columnDelta, -minColumn, composer.Playfield.TotalColumns - 1 - maxColumn);
|
||||
columnDelta = Math.Clamp(columnDelta, -minColumn, maniaPlayfield.TotalColumns - 1 - maxColumn);
|
||||
|
||||
foreach (var obj in SelectedHitObjects.OfType<ManiaHitObject>())
|
||||
obj.Column += columnDelta;
|
||||
|
@ -1,20 +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.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public class ManiaSnapResult : SnapResult
|
||||
{
|
||||
public readonly Column Column;
|
||||
|
||||
public ManiaSnapResult(Vector2 screenSpacePosition, double time, Column column)
|
||||
: base(screenSpacePosition, time)
|
||||
{
|
||||
Column = column;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@ -91,11 +92,11 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
|
||||
}
|
||||
|
||||
protected override void CreateNestedHitObjects()
|
||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||
{
|
||||
base.CreateNestedHitObjects();
|
||||
base.CreateNestedHitObjects(cancellationToken);
|
||||
|
||||
createTicks();
|
||||
createTicks(cancellationToken);
|
||||
|
||||
AddNested(Head = new Note
|
||||
{
|
||||
@ -112,13 +113,15 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
});
|
||||
}
|
||||
|
||||
private void createTicks()
|
||||
private void createTicks(CancellationToken cancellationToken)
|
||||
{
|
||||
if (tickSpacing == 0)
|
||||
return;
|
||||
|
||||
for (double t = StartTime + tickSpacing; t <= EndTime - tickSpacing; t += tickSpacing)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
AddNested(new HoldNoteTick
|
||||
{
|
||||
StartTime = t,
|
||||
|
@ -17,7 +17,6 @@ using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
@ -143,54 +142,5 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
|
||||
// This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border
|
||||
=> DrawRectangle.Inflate(new Vector2(Stage.COLUMN_SPACING / 2, 0)).Contains(ToLocalSpace(screenSpacePos));
|
||||
|
||||
/// <summary>
|
||||
/// Given a time, return the screen space position within this column.
|
||||
/// </summary>
|
||||
public Vector2 ScreenSpacePositionAtTime(double time)
|
||||
{
|
||||
var pos = ScrollingInfo.Algorithm.PositionAt(time, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight);
|
||||
|
||||
switch (ScrollingInfo.Direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Down:
|
||||
// We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time.
|
||||
// The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position,
|
||||
// so when scrolling downwards the coordinates need to be flipped.
|
||||
pos = HitObjectContainer.DrawHeight - pos;
|
||||
|
||||
// Blueprints are centred on the mouse position, such that the hitobject position is anchored at the top or bottom of the blueprint depending on the scroll direction.
|
||||
pos -= DefaultNotePiece.NOTE_HEIGHT / 2;
|
||||
break;
|
||||
|
||||
case ScrollingDirection.Up:
|
||||
pos += DefaultNotePiece.NOTE_HEIGHT / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return HitObjectContainer.ToScreenSpace(new Vector2(HitObjectContainer.DrawWidth / 2, pos));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a position in screen space, return the time within this column.
|
||||
/// </summary>
|
||||
public double TimeAtScreenSpacePosition(Vector2 screenSpacePosition)
|
||||
{
|
||||
// convert to local space of column so we can snap and fetch correct location.
|
||||
Vector2 localPosition = HitObjectContainer.ToLocalSpace(screenSpacePosition);
|
||||
|
||||
switch (ScrollingInfo.Direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Down:
|
||||
// as above
|
||||
localPosition.Y = HitObjectContainer.DrawHeight - localPosition.Y;
|
||||
break;
|
||||
}
|
||||
|
||||
// offset for the fact that blueprints are centered, as above.
|
||||
localPosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2;
|
||||
|
||||
return ScrollingInfo.Algorithm.TimeAt(localPosition.Y, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
||||
{
|
||||
protected new T HitObject => (T)DrawableObject.HitObject;
|
||||
|
||||
protected override bool AlwaysShowWhenSelected => true;
|
||||
|
||||
protected OsuSelectionBlueprint(DrawableHitObject drawableObject)
|
||||
: base(drawableObject)
|
||||
{
|
||||
|
@ -4,6 +4,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
@ -12,6 +16,7 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
@ -32,9 +37,80 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
new SpinnerCompositionTool()
|
||||
};
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
LayerBelowRuleset.Add(distanceSnapGridContainer = new Container { RelativeSizeAxes = Axes.Both });
|
||||
|
||||
EditorBeatmap.SelectedHitObjects.CollectionChanged += (_, __) => updateDistanceSnapGrid();
|
||||
EditorBeatmap.PlacementObject.ValueChanged += _ => updateDistanceSnapGrid();
|
||||
}
|
||||
|
||||
protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(HitObjects);
|
||||
|
||||
protected override DistanceSnapGrid CreateDistanceSnapGrid(IEnumerable<HitObject> selectedHitObjects)
|
||||
private DistanceSnapGrid distanceSnapGrid;
|
||||
private Container distanceSnapGridContainer;
|
||||
|
||||
private readonly Cached distanceSnapGridCache = new Cached();
|
||||
private double? lastDistanceSnapGridTime;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!(BlueprintContainer.CurrentTool is SelectTool))
|
||||
{
|
||||
if (EditorClock.CurrentTime != lastDistanceSnapGridTime)
|
||||
{
|
||||
distanceSnapGridCache.Invalidate();
|
||||
lastDistanceSnapGridTime = EditorClock.CurrentTime;
|
||||
}
|
||||
|
||||
if (!distanceSnapGridCache.IsValid)
|
||||
updateDistanceSnapGrid();
|
||||
}
|
||||
}
|
||||
|
||||
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||
{
|
||||
if (distanceSnapGrid == null)
|
||||
return base.SnapScreenSpacePositionToValidTime(screenSpacePosition);
|
||||
|
||||
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
|
||||
|
||||
return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, PlayfieldAtScreenSpacePosition(screenSpacePosition));
|
||||
}
|
||||
|
||||
private void updateDistanceSnapGrid()
|
||||
{
|
||||
distanceSnapGridContainer.Clear();
|
||||
distanceSnapGridCache.Invalidate();
|
||||
|
||||
switch (BlueprintContainer.CurrentTool)
|
||||
{
|
||||
case SelectTool _:
|
||||
if (!EditorBeatmap.SelectedHitObjects.Any())
|
||||
return;
|
||||
|
||||
distanceSnapGrid = createDistanceSnapGrid(EditorBeatmap.SelectedHitObjects);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!CursorInPlacementArea)
|
||||
return;
|
||||
|
||||
distanceSnapGrid = createDistanceSnapGrid(Enumerable.Empty<HitObject>());
|
||||
break;
|
||||
}
|
||||
|
||||
if (distanceSnapGrid != null)
|
||||
{
|
||||
distanceSnapGridContainer.Add(distanceSnapGrid);
|
||||
distanceSnapGridCache.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
private DistanceSnapGrid createDistanceSnapGrid(IEnumerable<HitObject> selectedHitObjects)
|
||||
{
|
||||
if (BlueprintContainer.CurrentTool is SpinnerCompositionTool)
|
||||
return null;
|
||||
@ -42,7 +118,8 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
var objects = selectedHitObjects.ToList();
|
||||
|
||||
if (objects.Count == 0)
|
||||
return createGrid(h => h.StartTime <= EditorClock.CurrentTime);
|
||||
// use accurate time value to give more instantaneous feedback to the user.
|
||||
return createGrid(h => h.StartTime <= EditorClock.CurrentTimeAccurate);
|
||||
|
||||
double minTime = objects.Min(h => h.StartTime);
|
||||
return createGrid(h => h.StartTime < minTime, objects.Count + 1);
|
||||
|
@ -6,6 +6,7 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -133,12 +134,12 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier;
|
||||
}
|
||||
|
||||
protected override void CreateNestedHitObjects()
|
||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||
{
|
||||
base.CreateNestedHitObjects();
|
||||
base.CreateNestedHitObjects(cancellationToken);
|
||||
|
||||
foreach (var e in
|
||||
SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset))
|
||||
SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset, cancellationToken))
|
||||
{
|
||||
switch (e.Type)
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@ -73,17 +74,17 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
overallDifficulty = difficulty.OverallDifficulty;
|
||||
}
|
||||
|
||||
protected override void CreateNestedHitObjects()
|
||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||
{
|
||||
createTicks();
|
||||
createTicks(cancellationToken);
|
||||
|
||||
RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * overallDifficulty);
|
||||
RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * overallDifficulty);
|
||||
|
||||
base.CreateNestedHitObjects();
|
||||
base.CreateNestedHitObjects(cancellationToken);
|
||||
}
|
||||
|
||||
private void createTicks()
|
||||
private void createTicks(CancellationToken cancellationToken)
|
||||
{
|
||||
if (tickSpacing == 0)
|
||||
return;
|
||||
@ -92,6 +93,8 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
|
||||
for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
AddNested(new DrumRollTick
|
||||
{
|
||||
FirstTick = first,
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -29,12 +30,15 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
set => throw new NotSupportedException($"{nameof(Swell)} cannot be a strong hitobject.");
|
||||
}
|
||||
|
||||
protected override void CreateNestedHitObjects()
|
||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||
{
|
||||
base.CreateNestedHitObjects();
|
||||
base.CreateNestedHitObjects(cancellationToken);
|
||||
|
||||
for (int i = 0; i < RequiredHits; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
AddNested(new SwellTick());
|
||||
}
|
||||
}
|
||||
|
||||
public override Judgement CreateJudgement() => new TaikoSwellJudgement();
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Threading;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -32,9 +33,9 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
/// </summary>
|
||||
public virtual bool IsStrong { get; set; }
|
||||
|
||||
protected override void CreateNestedHitObjects()
|
||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||
{
|
||||
base.CreateNestedHitObjects();
|
||||
base.CreateNestedHitObjects(cancellationToken);
|
||||
|
||||
if (IsStrong)
|
||||
AddNested(new StrongHitObject { StartTime = this.GetEndTime() });
|
||||
|
@ -156,7 +156,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||
|
||||
// ReSharper disable once AccessToModifiedClosure
|
||||
manager.ItemAdded.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount));
|
||||
manager.ItemUpdated.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount));
|
||||
manager.ItemRemoved.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount));
|
||||
|
||||
var imported = await LoadOszIntoOsu(osu);
|
||||
@ -166,7 +166,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
imported.Hash += "-changed";
|
||||
manager.Update(imported);
|
||||
|
||||
Assert.AreEqual(0, itemAddRemoveFireCount -= 2);
|
||||
Assert.AreEqual(0, itemAddRemoveFireCount -= 1);
|
||||
|
||||
checkBeatmapSetCount(osu, 1);
|
||||
checkBeatmapCount(osu, 12);
|
||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
[Test]
|
||||
public void TestSingleSpan()
|
||||
{
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null).ToArray();
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null, default).ToArray();
|
||||
|
||||
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
|
||||
Assert.That(events[0].Time, Is.EqualTo(start_time));
|
||||
@ -31,7 +31,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
[Test]
|
||||
public void TestRepeat()
|
||||
{
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null).ToArray();
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null, default).ToArray();
|
||||
|
||||
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
|
||||
Assert.That(events[0].Time, Is.EqualTo(start_time));
|
||||
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
[Test]
|
||||
public void TestNonEvenTicks()
|
||||
{
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null).ToArray();
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null, default).ToArray();
|
||||
|
||||
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
|
||||
Assert.That(events[0].Time, Is.EqualTo(start_time));
|
||||
@ -85,7 +85,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
[Test]
|
||||
public void TestLegacyLastTickOffset()
|
||||
{
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100).ToArray();
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100, default).ToArray();
|
||||
|
||||
Assert.That(events[2].Type, Is.EqualTo(SliderEventType.LegacyLastTick));
|
||||
Assert.That(events[2].Time, Is.EqualTo(900));
|
||||
@ -97,7 +97,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
const double velocity = 5;
|
||||
const double min_distance = velocity * 10;
|
||||
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0).ToArray();
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0, default).ToArray();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
|
@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
/// Check if <see cref="PlayerLoader"/> properly triggers the visual settings preview when a user hovers over the visual settings panel.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void PlayerLoaderSettingsHoverTest()
|
||||
public void TestPlayerLoaderSettingsHover()
|
||||
{
|
||||
setupUserSettings();
|
||||
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new LoadBlockingTestPlayer { BlockLoad = true })));
|
||||
@ -79,11 +79,9 @@ namespace osu.Game.Tests.Visual.Background
|
||||
InputManager.MoveMouseTo(playerLoader.ScreenPos);
|
||||
InputManager.MoveMouseTo(playerLoader.VisualSettingsPos);
|
||||
});
|
||||
waitForDim();
|
||||
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddStep("Stop background preview", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
|
||||
waitForDim();
|
||||
AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
|
||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -92,20 +90,19 @@ namespace osu.Game.Tests.Visual.Background
|
||||
/// We need to check that in this scenario, the dim and blur is still properly applied after entering player.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void PlayerLoaderTransitionTest()
|
||||
public void TestPlayerLoaderTransition()
|
||||
{
|
||||
performFullSetup();
|
||||
AddStep("Trigger hover event", () => playerLoader.TriggerOnHover());
|
||||
AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
|
||||
waitForDim();
|
||||
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make sure the background is fully invisible (Alpha == 0) when the background should be disabled by the storyboard.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StoryboardBackgroundVisibilityTest()
|
||||
public void TestStoryboardBackgroundVisibility()
|
||||
{
|
||||
performFullSetup();
|
||||
createFakeStoryboard();
|
||||
@ -114,52 +111,46 @@ namespace osu.Game.Tests.Visual.Background
|
||||
player.ReplacesBackground.Value = true;
|
||||
player.StoryboardEnabled.Value = true;
|
||||
});
|
||||
waitForDim();
|
||||
AddAssert("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible);
|
||||
AddUntilStep("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible);
|
||||
AddStep("Disable Storyboard", () =>
|
||||
{
|
||||
player.ReplacesBackground.Value = false;
|
||||
player.StoryboardEnabled.Value = false;
|
||||
});
|
||||
waitForDim();
|
||||
AddAssert("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && !player.IsStoryboardVisible);
|
||||
AddUntilStep("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && !player.IsStoryboardVisible);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When exiting player, the screen that it suspends/exits to needs to have a fully visible (Alpha == 1) background.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StoryboardTransitionTest()
|
||||
public void TestStoryboardTransition()
|
||||
{
|
||||
performFullSetup();
|
||||
createFakeStoryboard();
|
||||
AddStep("Exit to song select", () => player.Exit());
|
||||
waitForDim();
|
||||
AddAssert("Background is visible", () => songSelect.IsBackgroundVisible());
|
||||
AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure <see cref="UserDimContainer"/> is properly accepting user-defined visual changes for a background.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DisableUserDimBackgroundTest()
|
||||
public void TestDisableUserDimBackground()
|
||||
{
|
||||
performFullSetup();
|
||||
waitForDim();
|
||||
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddStep("Enable user dim", () => songSelect.DimEnabled.Value = false);
|
||||
waitForDim();
|
||||
AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled());
|
||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled());
|
||||
AddStep("Disable user dim", () => songSelect.DimEnabled.Value = true);
|
||||
waitForDim();
|
||||
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure <see cref="UserDimContainer"/> is properly accepting user-defined visual changes for a storyboard.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DisableUserDimStoryboardTest()
|
||||
public void TestDisableUserDimStoryboard()
|
||||
{
|
||||
performFullSetup();
|
||||
createFakeStoryboard();
|
||||
@ -170,41 +161,36 @@ namespace osu.Game.Tests.Visual.Background
|
||||
});
|
||||
AddStep("Enable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = true);
|
||||
AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f);
|
||||
waitForDim();
|
||||
AddAssert("Storyboard is invisible", () => !player.IsStoryboardVisible);
|
||||
AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible);
|
||||
AddStep("Disable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = false);
|
||||
waitForDim();
|
||||
AddAssert("Storyboard is visible", () => player.IsStoryboardVisible);
|
||||
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the visual settings container retains dim and blur when pausing
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void PauseTest()
|
||||
public void TestPause()
|
||||
{
|
||||
performFullSetup(true);
|
||||
AddStep("Pause", () => player.Pause());
|
||||
waitForDim();
|
||||
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddStep("Unpause", () => player.Resume());
|
||||
waitForDim();
|
||||
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the visual settings container removes user dim when suspending <see cref="Player"/> for <see cref="ResultsScreen"/>
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TransitionTest()
|
||||
public void TestTransition()
|
||||
{
|
||||
performFullSetup();
|
||||
FadeAccessibleResults results = null;
|
||||
AddStep("Transition to Results", () => player.Push(results =
|
||||
new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } })));
|
||||
AddUntilStep("Wait for results is current", () => results.IsCurrentScreen());
|
||||
waitForDim();
|
||||
AddAssert("Screen is undimmed, original background retained", () =>
|
||||
AddUntilStep("Screen is undimmed, original background retained", () =>
|
||||
songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect());
|
||||
}
|
||||
|
||||
@ -212,32 +198,27 @@ namespace osu.Game.Tests.Visual.Background
|
||||
/// Check if background gets undimmed and unblurred when leaving <see cref="Player"/> for <see cref="PlaySongSelect"/>
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TransitionOutTest()
|
||||
public void TestTransitionOut()
|
||||
{
|
||||
performFullSetup();
|
||||
AddStep("Exit to song select", () => player.Exit());
|
||||
waitForDim();
|
||||
AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBlurCorrect());
|
||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBlurCorrect());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if hovering on the visual settings dialogue after resuming from player still previews the background dim.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ResumeFromPlayerTest()
|
||||
public void TestResumeFromPlayer()
|
||||
{
|
||||
performFullSetup();
|
||||
AddStep("Move mouse to Visual Settings", () => InputManager.MoveMouseTo(playerLoader.VisualSettingsPos));
|
||||
AddStep("Resume PlayerLoader", () => player.Restart());
|
||||
waitForDim();
|
||||
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
|
||||
waitForDim();
|
||||
AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
|
||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
|
||||
}
|
||||
|
||||
private void waitForDim() => AddWaitStep("Wait for dim", 5);
|
||||
|
||||
private void createFakeStoryboard() => AddStep("Create storyboard", () =>
|
||||
{
|
||||
player.StoryboardEnabled.Value = false;
|
||||
|
@ -4,8 +4,8 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Components;
|
||||
using osuTK;
|
||||
|
||||
@ -17,9 +17,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
Dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
Dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
var clock = new EditorClock { IsCoupled = false };
|
||||
Dependencies.CacheAs(clock);
|
||||
|
||||
var playback = new PlaybackControl
|
||||
{
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@ -69,7 +68,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
private IBindable<WorkingBeatmap> beatmap { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
public AudioVisualiser()
|
||||
{
|
||||
@ -96,13 +95,15 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
base.Update();
|
||||
|
||||
if (beatmap.Value.Track.IsLoaded)
|
||||
marker.X = (float)(adjustableClock.CurrentTime / beatmap.Value.Track.Length);
|
||||
marker.X = (float)(editorClock.CurrentTime / beatmap.Value.Track.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private class StartStopButton : OsuButton
|
||||
{
|
||||
private IAdjustableClock adjustableClock;
|
||||
[Resolved]
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
private bool started;
|
||||
|
||||
public StartStopButton()
|
||||
@ -114,22 +115,16 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
Action = onClick;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAdjustableClock adjustableClock)
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
}
|
||||
|
||||
private void onClick()
|
||||
{
|
||||
if (started)
|
||||
{
|
||||
adjustableClock.Stop();
|
||||
editorClock.Stop();
|
||||
Text = "Start";
|
||||
}
|
||||
else
|
||||
{
|
||||
adjustableClock.Start();
|
||||
editorClock.Start();
|
||||
Text = "Stop";
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,6 @@ using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
|
||||
using ZipArchive = SharpCompress.Archives.Zip.ZipArchive;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -66,7 +65,6 @@ namespace osu.Game.Beatmaps
|
||||
private readonly AudioManager audioManager;
|
||||
private readonly GameHost host;
|
||||
private readonly BeatmapOnlineLookupQueue onlineLookupQueue;
|
||||
private readonly Storage exportStorage;
|
||||
|
||||
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null,
|
||||
WorkingBeatmap defaultBeatmap = null)
|
||||
@ -83,7 +81,6 @@ namespace osu.Game.Beatmaps
|
||||
beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference<BeatmapInfo>(b);
|
||||
|
||||
onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
|
||||
exportStorage = storage.GetStorageForDirectory("exports");
|
||||
}
|
||||
|
||||
protected override ArchiveDownloadRequest<BeatmapSetInfo> CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) =>
|
||||
@ -214,26 +211,6 @@ namespace osu.Game.Beatmaps
|
||||
workingCache.Remove(working);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports a <see cref="BeatmapSetInfo"/> to an .osz package.
|
||||
/// </summary>
|
||||
/// <param name="set">The <see cref="BeatmapSetInfo"/> to export.</param>
|
||||
public void Export(BeatmapSetInfo set)
|
||||
{
|
||||
var localSet = QueryBeatmapSet(s => s.ID == set.ID);
|
||||
|
||||
using (var archive = ZipArchive.Create())
|
||||
{
|
||||
foreach (var file in localSet.Files)
|
||||
archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath));
|
||||
|
||||
using (var outputStream = exportStorage.GetStream($"{set}.osz", FileAccess.Write, FileMode.Create))
|
||||
archive.SaveTo(outputStream);
|
||||
|
||||
exportStorage.OpenInNativeExplorer();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly WeakList<WorkingBeatmap> workingCache = new WeakList<WorkingBeatmap>();
|
||||
|
||||
/// <summary>
|
||||
|
@ -129,12 +129,19 @@ namespace osu.Game.Beatmaps
|
||||
processor?.PreProcess();
|
||||
|
||||
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
|
||||
foreach (var obj in converted.HitObjects)
|
||||
try
|
||||
{
|
||||
if (cancellationSource.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
foreach (var obj in converted.HitObjects)
|
||||
{
|
||||
if (cancellationSource.IsCancellationRequested)
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
|
||||
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
|
||||
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty, cancellationSource.Token);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw new BeatmapLoadTimeoutException(BeatmapInfo);
|
||||
}
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToHitObject>())
|
||||
|
@ -22,6 +22,7 @@ using osu.Game.IO.Archives;
|
||||
using osu.Game.IPC;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Utils;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
using FileInfo = osu.Game.IO.FileInfo;
|
||||
|
||||
@ -54,12 +55,12 @@ namespace osu.Game.Database
|
||||
public Action<Notification> PostNotification { protected get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a new <typeparamref name="TModel"/> becomes available in the database.
|
||||
/// Fired when a new or updated <typeparamref name="TModel"/> becomes available in the database.
|
||||
/// This is not guaranteed to run on the update thread.
|
||||
/// </summary>
|
||||
public IBindable<WeakReference<TModel>> ItemAdded => itemAdded;
|
||||
public IBindable<WeakReference<TModel>> ItemUpdated => itemUpdated;
|
||||
|
||||
private readonly Bindable<WeakReference<TModel>> itemAdded = new Bindable<WeakReference<TModel>>();
|
||||
private readonly Bindable<WeakReference<TModel>> itemUpdated = new Bindable<WeakReference<TModel>>();
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a <typeparamref name="TModel"/> is removed from the database.
|
||||
@ -82,14 +83,18 @@ namespace osu.Game.Database
|
||||
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
|
||||
private ArchiveImportIPCChannel ipc;
|
||||
|
||||
private readonly Storage exportStorage;
|
||||
|
||||
protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes<TModel, TFileModel> modelStore, IIpcHost importHost = null)
|
||||
{
|
||||
ContextFactory = contextFactory;
|
||||
|
||||
ModelStore = modelStore;
|
||||
ModelStore.ItemAdded += item => handleEvent(() => itemAdded.Value = new WeakReference<TModel>(item));
|
||||
ModelStore.ItemUpdated += item => handleEvent(() => itemUpdated.Value = new WeakReference<TModel>(item));
|
||||
ModelStore.ItemRemoved += item => handleEvent(() => itemRemoved.Value = new WeakReference<TModel>(item));
|
||||
|
||||
exportStorage = storage.GetStorageForDirectory("exports");
|
||||
|
||||
Files = new FileStore(contextFactory, storage);
|
||||
|
||||
if (importHost != null)
|
||||
@ -369,6 +374,29 @@ namespace osu.Game.Database
|
||||
return item;
|
||||
}, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap();
|
||||
|
||||
/// <summary>
|
||||
/// Exports an item to a legacy (.zip based) package.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to export.</param>
|
||||
public void Export(TModel item)
|
||||
{
|
||||
var retrievedItem = ModelStore.ConsumableItems.FirstOrDefault(s => s.ID == item.ID);
|
||||
|
||||
if (retrievedItem == null)
|
||||
throw new ArgumentException("Specified model could not be found", nameof(item));
|
||||
|
||||
using (var archive = ZipArchive.Create())
|
||||
{
|
||||
foreach (var file in retrievedItem.Files)
|
||||
archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath));
|
||||
|
||||
using (var outputStream = exportStorage.GetStream($"{getValidFilename(item.ToString())}{HandledExtensions.First()}", FileAccess.Write, FileMode.Create))
|
||||
archive.SaveTo(outputStream);
|
||||
|
||||
exportStorage.OpenInNativeExplorer();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateFile(TModel model, TFileModel file, Stream contents)
|
||||
{
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
@ -710,5 +738,12 @@ namespace osu.Game.Database
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string getValidFilename(string filename)
|
||||
{
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
filename = filename.Replace(c, '_');
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Database
|
||||
public interface IModelManager<TModel>
|
||||
where TModel : class
|
||||
{
|
||||
IBindable<WeakReference<TModel>> ItemAdded { get; }
|
||||
IBindable<WeakReference<TModel>> ItemUpdated { get; }
|
||||
|
||||
IBindable<WeakReference<TModel>> ItemRemoved { get; }
|
||||
}
|
||||
|
@ -16,7 +16,14 @@ namespace osu.Game.Database
|
||||
public abstract class MutableDatabaseBackedStore<T> : DatabaseBackedStore
|
||||
where T : class, IHasPrimaryKey, ISoftDelete
|
||||
{
|
||||
public event Action<T> ItemAdded;
|
||||
/// <summary>
|
||||
/// Fired when an item was added or updated.
|
||||
/// </summary>
|
||||
public event Action<T> ItemUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when an item was removed.
|
||||
/// </summary>
|
||||
public event Action<T> ItemRemoved;
|
||||
|
||||
protected MutableDatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null)
|
||||
@ -41,7 +48,7 @@ namespace osu.Game.Database
|
||||
context.Attach(item);
|
||||
}
|
||||
|
||||
ItemAdded?.Invoke(item);
|
||||
ItemUpdated?.Invoke(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -53,8 +60,7 @@ namespace osu.Game.Database
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
usage.Context.Update(item);
|
||||
|
||||
ItemRemoved?.Invoke(item);
|
||||
ItemAdded?.Invoke(item);
|
||||
ItemUpdated?.Invoke(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -91,7 +97,7 @@ namespace osu.Game.Database
|
||||
item.DeletePending = false;
|
||||
}
|
||||
|
||||
ItemAdded?.Invoke(item);
|
||||
ItemUpdated?.Invoke(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ namespace osu.Game.IO
|
||||
|
||||
public override void DeleteDatabase(string name) => UnderlyingStorage.DeleteDatabase(MutatePath(name));
|
||||
|
||||
public override void OpenInNativeExplorer() => UnderlyingStorage.OpenInNativeExplorer();
|
||||
public override void OpenPathInNativeExplorer(string path) => UnderlyingStorage.OpenPathInNativeExplorer(MutatePath(path));
|
||||
|
||||
public override Storage GetStorageForDirectory(string path)
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Online
|
||||
Model.Value = model;
|
||||
}
|
||||
|
||||
private IBindable<WeakReference<TModel>> managerAdded;
|
||||
private IBindable<WeakReference<TModel>> managedUpdated;
|
||||
private IBindable<WeakReference<TModel>> managerRemoved;
|
||||
private IBindable<WeakReference<ArchiveDownloadRequest<TModel>>> managerDownloadBegan;
|
||||
private IBindable<WeakReference<ArchiveDownloadRequest<TModel>>> managerDownloadFailed;
|
||||
@ -56,8 +56,8 @@ namespace osu.Game.Online
|
||||
managerDownloadBegan.BindValueChanged(downloadBegan);
|
||||
managerDownloadFailed = manager.DownloadFailed.GetBoundCopy();
|
||||
managerDownloadFailed.BindValueChanged(downloadFailed);
|
||||
managerAdded = manager.ItemAdded.GetBoundCopy();
|
||||
managerAdded.BindValueChanged(itemAdded);
|
||||
managedUpdated = manager.ItemUpdated.GetBoundCopy();
|
||||
managedUpdated.BindValueChanged(itemUpdated);
|
||||
managerRemoved = manager.ItemRemoved.GetBoundCopy();
|
||||
managerRemoved.BindValueChanged(itemRemoved);
|
||||
}
|
||||
@ -128,7 +128,7 @@ namespace osu.Game.Online
|
||||
|
||||
private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null));
|
||||
|
||||
private void itemAdded(ValueChangedEvent<WeakReference<TModel>> weakItem)
|
||||
private void itemUpdated(ValueChangedEvent<WeakReference<TModel>> weakItem)
|
||||
{
|
||||
if (weakItem.NewValue.TryGetTarget(out var item))
|
||||
setDownloadStateFromManager(item, DownloadState.LocallyAvailable);
|
||||
|
@ -192,7 +192,7 @@ namespace osu.Game
|
||||
ScoreManager.Delete(getBeatmapScores(item), true);
|
||||
});
|
||||
|
||||
BeatmapManager.ItemAdded.BindValueChanged(i =>
|
||||
BeatmapManager.ItemUpdated.BindValueChanged(i =>
|
||||
{
|
||||
if (i.NewValue.TryGetTarget(out var item))
|
||||
ScoreManager.Undelete(getBeatmapScores(item), true);
|
||||
|
@ -60,14 +60,14 @@ namespace osu.Game.Overlays
|
||||
[Resolved(canBeNull: true)]
|
||||
private OnScreenDisplay onScreenDisplay { get; set; }
|
||||
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> managerAdded;
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> managerRemoved;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
managerAdded = beatmaps.ItemAdded.GetBoundCopy();
|
||||
managerAdded.BindValueChanged(beatmapAdded);
|
||||
managerUpdated = beatmaps.ItemUpdated.GetBoundCopy();
|
||||
managerUpdated.BindValueChanged(beatmapUpdated);
|
||||
managerRemoved = beatmaps.ItemRemoved.GetBoundCopy();
|
||||
managerRemoved.BindValueChanged(beatmapRemoved);
|
||||
|
||||
@ -98,14 +98,14 @@ namespace osu.Game.Overlays
|
||||
/// </summary>
|
||||
public bool IsPlaying => current?.Track.IsRunning ?? false;
|
||||
|
||||
private void beatmapAdded(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
|
||||
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
|
||||
{
|
||||
if (weakSet.NewValue.TryGetTarget(out var set))
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
if (!beatmapSets.Contains(set))
|
||||
beatmapSets.Add(set);
|
||||
beatmapSets.Remove(set);
|
||||
beatmapSets.Add(set);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Skinning;
|
||||
@ -31,16 +32,18 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; }
|
||||
|
||||
private IBindable<WeakReference<SkinInfo>> managerAdded;
|
||||
private IBindable<WeakReference<SkinInfo>> managerUpdated;
|
||||
private IBindable<WeakReference<SkinInfo>> managerRemoved;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
FlowContent.Spacing = new Vector2(0, 5);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
skinDropdown = new SkinSettingsDropdown(),
|
||||
new ExportSkinButton(),
|
||||
new SettingsSlider<float, SizeSlider>
|
||||
{
|
||||
LabelText = "Menu cursor size",
|
||||
@ -70,8 +73,8 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
},
|
||||
};
|
||||
|
||||
managerAdded = skins.ItemAdded.GetBoundCopy();
|
||||
managerAdded.BindValueChanged(itemAdded);
|
||||
managerUpdated = skins.ItemUpdated.GetBoundCopy();
|
||||
managerUpdated.BindValueChanged(itemUpdated);
|
||||
|
||||
managerRemoved = skins.ItemRemoved.GetBoundCopy();
|
||||
managerRemoved.BindValueChanged(itemRemoved);
|
||||
@ -89,10 +92,10 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
dropdownBindable.BindValueChanged(skin => configBindable.Value = skin.NewValue.ID);
|
||||
}
|
||||
|
||||
private void itemAdded(ValueChangedEvent<WeakReference<SkinInfo>> weakItem)
|
||||
private void itemUpdated(ValueChangedEvent<WeakReference<SkinInfo>> weakItem)
|
||||
{
|
||||
if (weakItem.NewValue.TryGetTarget(out var item))
|
||||
Schedule(() => skinDropdown.Items = skinDropdown.Items.Append(item).ToArray());
|
||||
Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToArray());
|
||||
}
|
||||
|
||||
private void itemRemoved(ValueChangedEvent<WeakReference<SkinInfo>> weakItem)
|
||||
@ -117,5 +120,35 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
protected override DropdownMenu CreateMenu() => base.CreateMenu().With(m => m.MaxHeight = 200);
|
||||
}
|
||||
}
|
||||
|
||||
private class ExportSkinButton : SettingsButton
|
||||
{
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; }
|
||||
|
||||
private Bindable<Skin> currentSkin;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Text = "Export selected skin";
|
||||
Action = export;
|
||||
|
||||
currentSkin = skins.CurrentSkin.GetBoundCopy();
|
||||
currentSkin.BindValueChanged(skin => Enabled.Value = skin.NewValue.SkinInfo.ID > 0, true);
|
||||
}
|
||||
|
||||
private void export()
|
||||
{
|
||||
try
|
||||
{
|
||||
skins.Export(currentSkin.Value.SkinInfo);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log($"Could not export current skin: {e.Message}", level: LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,15 +3,14 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
@ -20,6 +19,7 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||
using osu.Game.Screens.Edit.Compose;
|
||||
@ -38,23 +38,21 @@ namespace osu.Game.Rulesets.Edit
|
||||
protected readonly Ruleset Ruleset;
|
||||
|
||||
[Resolved]
|
||||
protected IFrameBasedClock EditorClock { get; private set; }
|
||||
protected EditorClock EditorClock { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
protected EditorBeatmap EditorBeatmap { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
|
||||
[Resolved]
|
||||
protected IBeatSnapProvider BeatSnapProvider { get; private set; }
|
||||
|
||||
protected ComposeBlueprintContainer BlueprintContainer { get; private set; }
|
||||
|
||||
public override Playfield Playfield => drawableRulesetWrapper.Playfield;
|
||||
|
||||
private DrawableEditRulesetWrapper<TObject> drawableRulesetWrapper;
|
||||
private Container distanceSnapGridContainer;
|
||||
private DistanceSnapGrid distanceSnapGrid;
|
||||
private readonly List<Container> layerContainers = new List<Container>();
|
||||
|
||||
protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
private InputManager inputManager;
|
||||
|
||||
@ -67,7 +65,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IFrameBasedClock framedClock)
|
||||
private void load()
|
||||
{
|
||||
Config = Dependencies.Get<RulesetConfigCache>().GetConfigFor(Ruleset);
|
||||
|
||||
@ -75,7 +73,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
drawableRulesetWrapper = new DrawableEditRulesetWrapper<TObject>(CreateDrawableRuleset(Ruleset, EditorBeatmap.PlayableBeatmap))
|
||||
{
|
||||
Clock = framedClock,
|
||||
Clock = EditorClock,
|
||||
ProcessCustomClock = false
|
||||
};
|
||||
}
|
||||
@ -85,17 +83,6 @@ namespace osu.Game.Rulesets.Edit
|
||||
return;
|
||||
}
|
||||
|
||||
var layerBelowRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChildren(new Drawable[]
|
||||
{
|
||||
distanceSnapGridContainer = new Container { RelativeSizeAxes = Axes.Both },
|
||||
new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both }
|
||||
});
|
||||
|
||||
var layerAboveRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChild(BlueprintContainer = CreateBlueprintContainer());
|
||||
|
||||
layerContainers.Add(layerBelowRuleset);
|
||||
layerContainers.Add(layerAboveRuleset);
|
||||
|
||||
InternalChild = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -117,11 +104,19 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
Name = "Content",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
layerBelowRuleset,
|
||||
// layers below playfield
|
||||
drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChildren(new Drawable[]
|
||||
{
|
||||
LayerBelowRuleset,
|
||||
new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both }
|
||||
}),
|
||||
drawableRulesetWrapper,
|
||||
layerAboveRuleset
|
||||
// layers above playfield
|
||||
drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer()
|
||||
.WithChild(BlueprintContainer = CreateBlueprintContainer())
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -139,7 +134,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
setSelectTool();
|
||||
|
||||
BlueprintContainer.SelectionChanged += selectionChanged;
|
||||
EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
@ -165,42 +160,13 @@ namespace osu.Game.Rulesets.Edit
|
||||
inputManager = GetContainingInputManager();
|
||||
}
|
||||
|
||||
private double lastGridUpdateTime;
|
||||
|
||||
protected override void Update()
|
||||
private void selectionChanged(object sender, NotifyCollectionChangedEventArgs changedArgs)
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (EditorClock.CurrentTime != lastGridUpdateTime && !(BlueprintContainer.CurrentTool is SelectTool))
|
||||
showGridFor(Enumerable.Empty<HitObject>());
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
layerContainers.ForEach(l =>
|
||||
{
|
||||
l.Anchor = drawableRulesetWrapper.Playfield.Anchor;
|
||||
l.Origin = drawableRulesetWrapper.Playfield.Origin;
|
||||
l.Position = drawableRulesetWrapper.Playfield.Position;
|
||||
l.Size = drawableRulesetWrapper.Playfield.Size;
|
||||
});
|
||||
}
|
||||
|
||||
private void selectionChanged(IEnumerable<HitObject> selectedHitObjects)
|
||||
{
|
||||
var hitObjects = selectedHitObjects.ToArray();
|
||||
|
||||
if (hitObjects.Any())
|
||||
if (EditorBeatmap.SelectedHitObjects.Any())
|
||||
{
|
||||
// ensure in selection mode if a selection is made.
|
||||
setSelectTool();
|
||||
|
||||
showGridFor(hitObjects);
|
||||
}
|
||||
else
|
||||
distanceSnapGridContainer.Hide();
|
||||
}
|
||||
|
||||
private void setSelectTool() => toolboxCollection.Items.First().Select();
|
||||
@ -209,30 +175,12 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
BlueprintContainer.CurrentTool = tool;
|
||||
|
||||
if (tool is SelectTool)
|
||||
distanceSnapGridContainer.Hide();
|
||||
else
|
||||
{
|
||||
if (!(tool is SelectTool))
|
||||
EditorBeatmap.SelectedHitObjects.Clear();
|
||||
showGridFor(Enumerable.Empty<HitObject>());
|
||||
}
|
||||
}
|
||||
|
||||
private void showGridFor(IEnumerable<HitObject> selectedHitObjects)
|
||||
{
|
||||
distanceSnapGridContainer.Clear();
|
||||
distanceSnapGrid = CreateDistanceSnapGrid(selectedHitObjects);
|
||||
|
||||
if (distanceSnapGrid != null)
|
||||
{
|
||||
distanceSnapGridContainer.Child = distanceSnapGrid;
|
||||
distanceSnapGridContainer.Show();
|
||||
}
|
||||
|
||||
lastGridUpdateTime = EditorClock.CurrentTime;
|
||||
}
|
||||
|
||||
public override IEnumerable<DrawableHitObject> HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects;
|
||||
|
||||
public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position);
|
||||
|
||||
protected abstract IReadOnlyList<HitObjectCompositionTool> CompositionTools { get; }
|
||||
@ -254,23 +202,32 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
EditorBeatmap.Add(hitObject);
|
||||
|
||||
if (adjustableClock.CurrentTime < hitObject.StartTime)
|
||||
adjustableClock.Seek(hitObject.StartTime);
|
||||
if (EditorClock.CurrentTime < hitObject.StartTime)
|
||||
EditorClock.SeekTo(hitObject.StartTime);
|
||||
}
|
||||
|
||||
showGridFor(Enumerable.Empty<HitObject>());
|
||||
}
|
||||
|
||||
public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject);
|
||||
|
||||
protected virtual Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => drawableRulesetWrapper.Playfield;
|
||||
|
||||
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||
{
|
||||
if (distanceSnapGrid == null) return new SnapResult(screenSpacePosition, null);
|
||||
var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition);
|
||||
double? targetTime = null;
|
||||
|
||||
// TODO: move distance snap grid to OsuHitObjectComposer.
|
||||
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
|
||||
if (playfield is ScrollingPlayfield scrollingPlayfield)
|
||||
{
|
||||
targetTime = scrollingPlayfield.TimeAtScreenSpacePosition(screenSpacePosition);
|
||||
|
||||
return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time);
|
||||
// apply beat snapping
|
||||
targetTime = BeatSnapProvider.SnapTime(targetTime.Value);
|
||||
|
||||
// convert back to screen space
|
||||
screenSpacePosition = scrollingPlayfield.ScreenSpacePositionAtTime(targetTime.Value);
|
||||
}
|
||||
|
||||
return new SnapResult(screenSpacePosition, targetTime, playfield);
|
||||
}
|
||||
|
||||
public override float GetBeatSnapDistanceAt(double referenceTime)
|
||||
@ -306,11 +263,13 @@ namespace osu.Game.Rulesets.Edit
|
||||
[Cached(typeof(IPositionSnapProvider))]
|
||||
public abstract class HitObjectComposer : CompositeDrawable, IPositionSnapProvider
|
||||
{
|
||||
internal HitObjectComposer()
|
||||
protected HitObjectComposer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
public abstract Playfield Playfield { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All the <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
@ -321,14 +280,6 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// </summary>
|
||||
public abstract bool CursorInPlacementArea { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="DistanceSnapGrid"/> applicable for a <see cref="HitObject"/> selection.
|
||||
/// </summary>
|
||||
/// <param name="selectedHitObjects">The <see cref="HitObject"/> selection.</param>
|
||||
/// <returns>The <see cref="DistanceSnapGrid"/> for <paramref name="selectedHitObjects"/>. If empty, a grid is returned for the current point in time.</returns>
|
||||
[CanBeNull]
|
||||
protected virtual DistanceSnapGrid CreateDistanceSnapGrid([NotNull] IEnumerable<HitObject> selectedHitObjects) => null;
|
||||
|
||||
public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition);
|
||||
|
||||
public abstract float GetBeatSnapDistanceAt(double referenceTime);
|
||||
|
@ -15,7 +15,12 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// </summary>
|
||||
public readonly DrawableHitObject DrawableObject;
|
||||
|
||||
protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || State == SelectionState.Selected;
|
||||
/// <summary>
|
||||
/// Whether the blueprint should be shown even when the <see cref="DrawableObject"/> is not alive.
|
||||
/// </summary>
|
||||
protected virtual bool AlwaysShowWhenSelected => false;
|
||||
|
||||
protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected);
|
||||
|
||||
protected OverlaySelectionBlueprint(DrawableHitObject drawableObject)
|
||||
: base(drawableObject.HitObject)
|
||||
|
@ -1,15 +1,16 @@
|
||||
// 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.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Compose;
|
||||
using osuTK;
|
||||
|
||||
@ -30,10 +31,13 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// </summary>
|
||||
protected readonly HitObject HitObject;
|
||||
|
||||
protected IClock EditorClock { get; private set; }
|
||||
[Resolved(canBeNull: true)]
|
||||
protected EditorClock EditorClock { get; private set; }
|
||||
|
||||
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
private Bindable<double> startTimeBindable;
|
||||
|
||||
[Resolved]
|
||||
private IPlacementHandler placementHandler { get; set; }
|
||||
|
||||
@ -49,13 +53,12 @@ namespace osu.Game.Rulesets.Edit
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IBindable<WorkingBeatmap> beatmap, IAdjustableClock clock)
|
||||
private void load(IBindable<WorkingBeatmap> beatmap)
|
||||
{
|
||||
this.beatmap.BindTo(beatmap);
|
||||
|
||||
EditorClock = clock;
|
||||
|
||||
ApplyDefaultsToHitObject();
|
||||
startTimeBindable = HitObject.StartTimeBindable.GetBoundCopy();
|
||||
startTimeBindable.BindValueChanged(_ => ApplyDefaultsToHitObject(), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -81,9 +84,6 @@ namespace osu.Game.Rulesets.Edit
|
||||
PlacementActive = false;
|
||||
}
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private IFrameBasedClock editorClock { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the position of this <see cref="PlacementBlueprint"/> to a new screen-space position.
|
||||
/// </summary>
|
||||
@ -91,11 +91,11 @@ namespace osu.Game.Rulesets.Edit
|
||||
public virtual void UpdatePosition(SnapResult snapResult)
|
||||
{
|
||||
if (!PlacementActive)
|
||||
HitObject.StartTime = snapResult.Time ?? editorClock?.CurrentTime ?? Time.Current;
|
||||
HitObject.StartTime = snapResult.Time ?? EditorClock?.CurrentTime ?? Time.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes <see cref="Objects.HitObject.ApplyDefaults(ControlPointInfo,BeatmapDifficulty)"/>,
|
||||
/// Invokes <see cref="Objects.HitObject.ApplyDefaults(ControlPointInfo,BeatmapDifficulty, CancellationToken)"/>,
|
||||
/// refreshing <see cref="Objects.HitObject.NestedHitObjects"/> and parameters for the <see cref="HitObject"/>.
|
||||
/// </summary>
|
||||
protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.Value.Beatmap.ControlPointInfo, beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Rulesets.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Edit
|
||||
@ -20,10 +21,13 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// </summary>
|
||||
public double? Time;
|
||||
|
||||
public SnapResult(Vector2 screenSpacePosition, double? time)
|
||||
public readonly Playfield Playfield;
|
||||
|
||||
public SnapResult(Vector2 screenSpacePosition, double? time, Playfield playfield = null)
|
||||
{
|
||||
ScreenSpacePosition = screenSpacePosition;
|
||||
Time = time;
|
||||
Playfield = playfield;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.Bindables;
|
||||
@ -99,7 +100,8 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// </summary>
|
||||
/// <param name="controlPointInfo">The control points.</param>
|
||||
/// <param name="difficulty">The difficulty settings to use.</param>
|
||||
public void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
@ -108,7 +110,7 @@ namespace osu.Game.Rulesets.Objects
|
||||
|
||||
nestedHitObjects.Clear();
|
||||
|
||||
CreateNestedHitObjects();
|
||||
CreateNestedHitObjects(cancellationToken);
|
||||
|
||||
if (this is IHasComboInformation hasCombo)
|
||||
{
|
||||
@ -122,7 +124,7 @@ namespace osu.Game.Rulesets.Objects
|
||||
nestedHitObjects.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
|
||||
|
||||
foreach (var h in nestedHitObjects)
|
||||
h.ApplyDefaults(controlPointInfo, difficulty);
|
||||
h.ApplyDefaults(controlPointInfo, difficulty, cancellationToken);
|
||||
|
||||
DefaultsApplied?.Invoke(this);
|
||||
}
|
||||
@ -136,6 +138,14 @@ namespace osu.Game.Rulesets.Objects
|
||||
HitWindows?.SetDifficulty(difficulty.OverallDifficulty);
|
||||
}
|
||||
|
||||
protected virtual void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||
{
|
||||
// ReSharper disable once MethodSupportsCancellation (https://youtrack.jetbrains.com/issue/RIDER-44520)
|
||||
#pragma warning disable 618
|
||||
CreateNestedHitObjects();
|
||||
#pragma warning restore 618
|
||||
}
|
||||
|
||||
protected virtual void CreateNestedHitObjects()
|
||||
{
|
||||
}
|
||||
|
@ -4,13 +4,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects
|
||||
{
|
||||
public static class SliderEventGenerator
|
||||
{
|
||||
[Obsolete("Use the overload with cancellation support instead.")] // can be removed 20201115
|
||||
public static IEnumerable<SliderEventDescriptor> Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount,
|
||||
double? legacyLastTickOffset)
|
||||
{
|
||||
return Generate(startTime, spanDuration, velocity, tickDistance, totalDistance, spanCount, legacyLastTickOffset, default);
|
||||
}
|
||||
|
||||
// ReSharper disable once MethodOverloadWithOptionalParameter
|
||||
public static IEnumerable<SliderEventDescriptor> Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount,
|
||||
double? legacyLastTickOffset, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// A very lenient maximum length of a slider for ticks to be generated.
|
||||
// This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage.
|
||||
@ -37,7 +46,7 @@ namespace osu.Game.Rulesets.Objects
|
||||
var spanStartTime = startTime + span * spanDuration;
|
||||
var reversed = span % 2 == 1;
|
||||
|
||||
var ticks = generateTicks(span, spanStartTime, spanDuration, reversed, length, tickDistance, minDistanceFromEnd);
|
||||
var ticks = generateTicks(span, spanStartTime, spanDuration, reversed, length, tickDistance, minDistanceFromEnd, cancellationToken);
|
||||
|
||||
if (reversed)
|
||||
{
|
||||
@ -108,12 +117,15 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// <param name="length">The length of the path.</param>
|
||||
/// <param name="tickDistance">The distance between each tick.</param>
|
||||
/// <param name="minDistanceFromEnd">The distance from the end of the path at which ticks are not allowed to be added.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="SliderEventDescriptor"/> for each tick. If <paramref name="reversed"/> is true, the ticks will be returned in reverse-StartTime order.</returns>
|
||||
private static IEnumerable<SliderEventDescriptor> generateTicks(int spanIndex, double spanStartTime, double spanDuration, bool reversed, double length, double tickDistance,
|
||||
double minDistanceFromEnd)
|
||||
double minDistanceFromEnd, CancellationToken cancellationToken = default)
|
||||
{
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (d >= length - minDistanceFromEnd)
|
||||
break;
|
||||
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Layout;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
@ -78,6 +79,98 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
hitObjectInitialStateCache.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a position in screen space, return the time within this column.
|
||||
/// </summary>
|
||||
public double TimeAtScreenSpacePosition(Vector2 screenSpacePosition)
|
||||
{
|
||||
// convert to local space of column so we can snap and fetch correct location.
|
||||
Vector2 localPosition = ToLocalSpace(screenSpacePosition);
|
||||
|
||||
float position = 0;
|
||||
|
||||
switch (scrollingInfo.Direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
case ScrollingDirection.Down:
|
||||
position = localPosition.Y;
|
||||
break;
|
||||
|
||||
case ScrollingDirection.Right:
|
||||
case ScrollingDirection.Left:
|
||||
position = localPosition.X;
|
||||
break;
|
||||
}
|
||||
|
||||
flipPositionIfRequired(ref position);
|
||||
|
||||
return scrollingInfo.Algorithm.TimeAt(position, Time.Current, scrollingInfo.TimeRange.Value, getLength());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a time, return the screen space position within this column.
|
||||
/// </summary>
|
||||
public Vector2 ScreenSpacePositionAtTime(double time)
|
||||
{
|
||||
var pos = scrollingInfo.Algorithm.PositionAt(time, Time.Current, scrollingInfo.TimeRange.Value, getLength());
|
||||
|
||||
flipPositionIfRequired(ref pos);
|
||||
|
||||
switch (scrollingInfo.Direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
case ScrollingDirection.Down:
|
||||
return ToScreenSpace(new Vector2(getBreadth() / 2, pos));
|
||||
|
||||
default:
|
||||
return ToScreenSpace(new Vector2(pos, getBreadth() / 2));
|
||||
}
|
||||
}
|
||||
|
||||
private float getLength()
|
||||
{
|
||||
switch (scrollingInfo.Direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Left:
|
||||
case ScrollingDirection.Right:
|
||||
return DrawWidth;
|
||||
|
||||
default:
|
||||
return DrawHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private float getBreadth()
|
||||
{
|
||||
switch (scrollingInfo.Direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
case ScrollingDirection.Down:
|
||||
return DrawWidth;
|
||||
|
||||
default:
|
||||
return DrawHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private void flipPositionIfRequired(ref float position)
|
||||
{
|
||||
// We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time.
|
||||
// The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position,
|
||||
// so when scrolling downwards the coordinates need to be flipped.
|
||||
|
||||
switch (scrollingInfo.Direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Down:
|
||||
position = DrawHeight - position;
|
||||
break;
|
||||
|
||||
case ScrollingDirection.Right:
|
||||
position = DrawWidth - position;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void onDefaultsApplied(DrawableHitObject drawableObject)
|
||||
{
|
||||
// The cache may not exist if the hitobject state hasn't been computed yet (e.g. if the hitobject was added + defaults applied in the same frame).
|
||||
|
@ -4,6 +4,7 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
@ -23,6 +24,18 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
Direction.BindTo(ScrollingInfo.Direction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a position in screen space, return the time within this column.
|
||||
/// </summary>
|
||||
public virtual double TimeAtScreenSpacePosition(Vector2 screenSpacePosition) =>
|
||||
((ScrollingHitObjectContainer)HitObjectContainer).TimeAtScreenSpacePosition(screenSpacePosition);
|
||||
|
||||
/// <summary>
|
||||
/// Given a time, return the screen space position within this column.
|
||||
/// </summary>
|
||||
public virtual Vector2 ScreenSpacePositionAtTime(double time)
|
||||
=> ((ScrollingHitObjectContainer)HitObjectContainer).ScreenSpacePositionAtTime(time);
|
||||
|
||||
protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer();
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -26,7 +25,7 @@ namespace osu.Game.Screens.Edit.Components
|
||||
private IconButton playButton;
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
private readonly BindableNumber<double> tempo = new BindableDouble(1);
|
||||
|
||||
@ -87,17 +86,17 @@ namespace osu.Game.Screens.Edit.Components
|
||||
|
||||
private void togglePause()
|
||||
{
|
||||
if (adjustableClock.IsRunning)
|
||||
adjustableClock.Stop();
|
||||
if (editorClock.IsRunning)
|
||||
editorClock.Stop();
|
||||
else
|
||||
adjustableClock.Start();
|
||||
editorClock.Start();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
playButton.Icon = adjustableClock.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle;
|
||||
playButton.Icon = editorClock.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle;
|
||||
}
|
||||
|
||||
private class PlaybackTabControl : OsuTabControl<double>
|
||||
|
@ -5,7 +5,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components
|
||||
@ -15,7 +14,7 @@ namespace osu.Game.Screens.Edit.Components
|
||||
private readonly OsuSpriteText trackTimer;
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
public TimeInfoContainer()
|
||||
{
|
||||
@ -35,7 +34,7 @@ namespace osu.Game.Screens.Edit.Components
|
||||
{
|
||||
base.Update();
|
||||
|
||||
trackTimer.Text = TimeSpan.FromMilliseconds(adjustableClock.CurrentTime).ToString(@"mm\:ss\:fff");
|
||||
trackTimer.Text = TimeSpan.FromMilliseconds(editorClock.CurrentTime).ToString(@"mm\:ss\:fff");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
@ -20,14 +19,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
/// </summary>
|
||||
public class MarkerPart : TimelinePart
|
||||
{
|
||||
private readonly Drawable marker;
|
||||
private Drawable marker;
|
||||
|
||||
private readonly IAdjustableClock adjustableClock;
|
||||
[Resolved]
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
public MarkerPart(IAdjustableClock adjustableClock)
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
|
||||
Add(marker = new MarkerVisualisation());
|
||||
}
|
||||
|
||||
@ -59,14 +58,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
return;
|
||||
|
||||
float markerPos = Math.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth);
|
||||
adjustableClock.Seek(markerPos / DrawWidth * Beatmap.Value.Track.Length);
|
||||
editorClock.SeekTo(markerPos / DrawWidth * editorClock.TrackLength);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
marker.X = (float)adjustableClock.CurrentTime;
|
||||
marker.X = (float)editorClock.CurrentTime;
|
||||
}
|
||||
|
||||
protected override void LoadBeatmap(WorkingBeatmap beatmap)
|
||||
|
@ -6,7 +6,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
||||
|
||||
@ -18,11 +17,11 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
|
||||
public class SummaryTimeline : BottomBarContainer
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, IAdjustableClock adjustableClock)
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new MarkerPart(adjustableClock) { RelativeSizeAxes = Axes.Both },
|
||||
new MarkerPart { RelativeSizeAxes = Axes.Both },
|
||||
new ControlPointPart
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@ -14,7 +13,6 @@ using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -29,8 +27,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// </summary>
|
||||
public abstract class BlueprintContainer : CompositeDrawable, IKeyBindingHandler<PlatformAction>
|
||||
{
|
||||
public event Action<IEnumerable<HitObject>> SelectionChanged;
|
||||
|
||||
protected DragBox DragBox { get; private set; }
|
||||
|
||||
protected Container<SelectionBlueprint> SelectionBlueprints { get; private set; }
|
||||
@ -41,13 +37,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
private IEditorChangeHandler changeHandler { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private EditorBeatmap beatmap { get; set; }
|
||||
|
||||
private readonly BindableList<HitObject> selectedHitObjects = new BindableList<HitObject>();
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private IPositionSnapProvider snapProvider { get; set; }
|
||||
|
||||
@ -88,8 +86,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
SelectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect();
|
||||
break;
|
||||
}
|
||||
|
||||
SelectionChanged?.Invoke(selectedHitObjects);
|
||||
};
|
||||
}
|
||||
|
||||
@ -149,7 +145,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
if (clickedBlueprint == null)
|
||||
return false;
|
||||
|
||||
adjustableClock?.Seek(clickedBlueprint.HitObject.StartTime);
|
||||
editorClock?.SeekTo(clickedBlueprint.HitObject.StartTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -74,9 +74,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// <summary>
|
||||
/// Handles the selected <see cref="DrawableHitObject"/>s being moved.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Just returning true is enough to allow <see cref="HitObject.StartTime"/> updates to take place.
|
||||
/// Custom implementation is only required if other attributes are to be considered, like changing columns.
|
||||
/// </remarks>
|
||||
/// <param name="moveEvent">The move event.</param>
|
||||
/// <returns>Whether any <see cref="DrawableHitObject"/>s were moved.</returns>
|
||||
public virtual bool HandleMovement(MoveSelectionEvent moveEvent) => false;
|
||||
/// <returns>
|
||||
/// Whether any <see cref="DrawableHitObject"/>s could be moved.
|
||||
/// Returning true will also propagate StartTime changes provided by the closest <see cref="IPositionSnapProvider.SnapScreenSpacePositionToValidTime"/>.
|
||||
/// </returns>
|
||||
public virtual bool HandleMovement(MoveSelectionEvent moveEvent) => true;
|
||||
|
||||
public bool OnPressed(PlatformAction action)
|
||||
{
|
||||
@ -244,14 +251,21 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
#region Context Menu
|
||||
|
||||
public virtual MenuItem[] ContextMenuItems
|
||||
public MenuItem[] ContextMenuItems
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!selectedBlueprints.Any(b => b.IsHovered))
|
||||
return Array.Empty<MenuItem>();
|
||||
|
||||
var items = new List<MenuItem>
|
||||
var items = new List<MenuItem>();
|
||||
|
||||
items.AddRange(GetContextMenuItemsForSelection(selectedBlueprints));
|
||||
|
||||
if (selectedBlueprints.Count == 1)
|
||||
items.AddRange(selectedBlueprints[0].ContextMenuItems);
|
||||
|
||||
items.AddRange(new[]
|
||||
{
|
||||
new OsuMenuItem("Sound")
|
||||
{
|
||||
@ -263,15 +277,20 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
}
|
||||
},
|
||||
new OsuMenuItem("Delete", MenuItemType.Destructive, deleteSelected),
|
||||
};
|
||||
|
||||
if (selectedBlueprints.Count == 1)
|
||||
items.AddRange(selectedBlueprints[0].ContextMenuItems);
|
||||
});
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provide context menu items relevant to current selection. Calling base is not required.
|
||||
/// </summary>
|
||||
/// <param name="selection">The current selection.</param>
|
||||
/// <returns>The relevant menu items.</returns>
|
||||
protected virtual IEnumerable<MenuItem> GetContextMenuItemsForSelection(IEnumerable<SelectionBlueprint> selection)
|
||||
=> Enumerable.Empty<MenuItem>();
|
||||
|
||||
private MenuItem createHitSampleMenuItem(string name, string sampleName)
|
||||
{
|
||||
return new TernaryStateMenuItem(name, MenuItemType.Standard, setHitSampleState)
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@ -25,7 +24,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
public readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
public Timeline()
|
||||
{
|
||||
@ -101,7 +100,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
Content.Margin = new MarginPadding { Horizontal = DrawWidth / 2 };
|
||||
|
||||
// This needs to happen after transforms are updated, but before the scroll position is updated in base.UpdateAfterChildren
|
||||
if (adjustableClock.IsRunning)
|
||||
if (editorClock.IsRunning)
|
||||
scrollToTrackTime();
|
||||
}
|
||||
|
||||
@ -111,21 +110,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
if (handlingDragInput)
|
||||
seekTrackToCurrent();
|
||||
else if (!adjustableClock.IsRunning)
|
||||
else if (!editorClock.IsRunning)
|
||||
{
|
||||
// The track isn't running. There are two cases we have to be wary of:
|
||||
// 1) The user flick-drags on this timeline: We want the track to follow us
|
||||
// 2) The user changes the track time through some other means (scrolling in the editor or overview timeline): We want to follow the track time
|
||||
|
||||
// The simplest way to cover both cases is by checking whether the scroll position has changed and the audio hasn't been changed externally
|
||||
if (Current != lastScrollPosition && adjustableClock.CurrentTime == lastTrackTime)
|
||||
if (Current != lastScrollPosition && editorClock.CurrentTime == lastTrackTime)
|
||||
seekTrackToCurrent();
|
||||
else
|
||||
scrollToTrackTime();
|
||||
}
|
||||
|
||||
lastScrollPosition = Current;
|
||||
lastTrackTime = adjustableClock.CurrentTime;
|
||||
lastTrackTime = editorClock.CurrentTime;
|
||||
}
|
||||
|
||||
private void seekTrackToCurrent()
|
||||
@ -133,7 +132,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
if (!track.IsLoaded)
|
||||
return;
|
||||
|
||||
adjustableClock.Seek(Current / Content.DrawWidth * track.Length);
|
||||
editorClock.Seek(Current / Content.DrawWidth * track.Length);
|
||||
}
|
||||
|
||||
private void scrollToTrackTime()
|
||||
@ -141,7 +140,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
if (!track.IsLoaded || track.Length == 0)
|
||||
return;
|
||||
|
||||
ScrollTo((float)(adjustableClock.CurrentTime / track.Length) * Content.DrawWidth, false);
|
||||
ScrollTo((float)(editorClock.CurrentTime / track.Length) * Content.DrawWidth, false);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
@ -164,15 +163,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
private void beginUserDrag()
|
||||
{
|
||||
handlingDragInput = true;
|
||||
trackWasPlaying = adjustableClock.IsRunning;
|
||||
adjustableClock.Stop();
|
||||
trackWasPlaying = editorClock.IsRunning;
|
||||
editorClock.Stop();
|
||||
}
|
||||
|
||||
private void endUserDrag()
|
||||
{
|
||||
handlingDragInput = false;
|
||||
if (trackWasPlaying)
|
||||
adjustableClock.Start();
|
||||
editorClock.Start();
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
|
@ -83,8 +83,8 @@ namespace osu.Game.Screens.Edit
|
||||
clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false };
|
||||
clock.ChangeSource(sourceClock);
|
||||
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs(clock);
|
||||
AddInternal(clock);
|
||||
|
||||
// todo: remove caching of this and consume via editorBeatmap?
|
||||
dependencies.Cache(beatDivisor);
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -13,7 +15,7 @@ namespace osu.Game.Screens.Edit
|
||||
/// <summary>
|
||||
/// A decoupled clock which adds editor-specific functionality, such as snapping to a user-defined beat divisor.
|
||||
/// </summary>
|
||||
public class EditorClock : DecoupleableInterpolatingFramedClock
|
||||
public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock
|
||||
{
|
||||
public readonly double TrackLength;
|
||||
|
||||
@ -21,12 +23,11 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private readonly BindableBeatDivisor beatDivisor;
|
||||
|
||||
public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor)
|
||||
{
|
||||
this.beatDivisor = beatDivisor;
|
||||
private readonly DecoupleableInterpolatingFramedClock underlyingClock;
|
||||
|
||||
ControlPointInfo = beatmap.Beatmap.ControlPointInfo;
|
||||
TrackLength = beatmap.Track.Length;
|
||||
public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor)
|
||||
: this(beatmap.Beatmap.ControlPointInfo, beatmap.Track.Length, beatDivisor)
|
||||
{
|
||||
}
|
||||
|
||||
public EditorClock(ControlPointInfo controlPointInfo, double trackLength, BindableBeatDivisor beatDivisor)
|
||||
@ -35,6 +36,13 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
ControlPointInfo = controlPointInfo;
|
||||
TrackLength = trackLength;
|
||||
|
||||
underlyingClock = new DecoupleableInterpolatingFramedClock();
|
||||
}
|
||||
|
||||
public EditorClock()
|
||||
: this(new ControlPointInfo(), 1000, new BindableBeatDivisor())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -79,20 +87,22 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private void seek(int direction, bool snapped, double amount = 1)
|
||||
{
|
||||
double current = CurrentTimeAccurate;
|
||||
|
||||
if (amount <= 0) throw new ArgumentException("Value should be greater than zero", nameof(amount));
|
||||
|
||||
var timingPoint = ControlPointInfo.TimingPointAt(CurrentTime);
|
||||
var timingPoint = ControlPointInfo.TimingPointAt(current);
|
||||
|
||||
if (direction < 0 && timingPoint.Time == CurrentTime)
|
||||
if (direction < 0 && timingPoint.Time == current)
|
||||
// When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into
|
||||
timingPoint = ControlPointInfo.TimingPointAt(CurrentTime - 1);
|
||||
timingPoint = ControlPointInfo.TimingPointAt(current - 1);
|
||||
|
||||
double seekAmount = timingPoint.BeatLength / beatDivisor.Value * amount;
|
||||
double seekTime = CurrentTime + seekAmount * direction;
|
||||
double seekTime = current + seekAmount * direction;
|
||||
|
||||
if (!snapped || ControlPointInfo.TimingPoints.Count == 0)
|
||||
{
|
||||
Seek(seekTime);
|
||||
SeekTo(seekTime);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -110,7 +120,7 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
// Due to the rounding above, we may end up on the current beat. This will effectively cause 0 seeking to happen, but we don't want this.
|
||||
// Instead, we'll go to the next beat in the direction when this is the case
|
||||
if (Precision.AlmostEquals(CurrentTime, seekTime))
|
||||
if (Precision.AlmostEquals(current, seekTime))
|
||||
{
|
||||
closestBeat += direction > 0 ? 1 : -1;
|
||||
seekTime = timingPoint.Time + closestBeat * seekAmount;
|
||||
@ -125,7 +135,97 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
// Ensure the sought point is within the boundaries
|
||||
seekTime = Math.Clamp(seekTime, 0, TrackLength);
|
||||
Seek(seekTime);
|
||||
SeekTo(seekTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current time of this clock, include any active transform seeks performed via <see cref="SeekTo"/>.
|
||||
/// </summary>
|
||||
public double CurrentTimeAccurate =>
|
||||
Transforms.OfType<TransformSeek>().FirstOrDefault()?.EndValue ?? CurrentTime;
|
||||
|
||||
public double CurrentTime => underlyingClock.CurrentTime;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ClearTransforms();
|
||||
underlyingClock.Reset();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
ClearTransforms();
|
||||
underlyingClock.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
underlyingClock.Stop();
|
||||
}
|
||||
|
||||
public bool Seek(double position)
|
||||
{
|
||||
ClearTransforms();
|
||||
return underlyingClock.Seek(position);
|
||||
}
|
||||
|
||||
public void ResetSpeedAdjustments() => underlyingClock.ResetSpeedAdjustments();
|
||||
|
||||
double IAdjustableClock.Rate
|
||||
{
|
||||
get => underlyingClock.Rate;
|
||||
set => underlyingClock.Rate = value;
|
||||
}
|
||||
|
||||
double IClock.Rate => underlyingClock.Rate;
|
||||
|
||||
public bool IsRunning => underlyingClock.IsRunning;
|
||||
|
||||
public void ProcessFrame() => underlyingClock.ProcessFrame();
|
||||
|
||||
public double ElapsedFrameTime => underlyingClock.ElapsedFrameTime;
|
||||
|
||||
public double FramesPerSecond => underlyingClock.FramesPerSecond;
|
||||
|
||||
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
|
||||
|
||||
public void ChangeSource(IClock source) => underlyingClock.ChangeSource(source);
|
||||
|
||||
public IClock Source => underlyingClock.Source;
|
||||
|
||||
public bool IsCoupled
|
||||
{
|
||||
get => underlyingClock.IsCoupled;
|
||||
set => underlyingClock.IsCoupled = value;
|
||||
}
|
||||
|
||||
private const double transform_time = 300;
|
||||
|
||||
public void SeekTo(double seekDestination)
|
||||
{
|
||||
if (IsRunning)
|
||||
Seek(seekDestination);
|
||||
else
|
||||
transformSeekTo(seekDestination, transform_time, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void transformSeekTo(double seek, double duration = 0, Easing easing = Easing.None)
|
||||
=> this.TransformTo(this.PopulateTransform(new TransformSeek(), seek, duration, easing));
|
||||
|
||||
private double currentTime
|
||||
{
|
||||
get => underlyingClock.CurrentTime;
|
||||
set => underlyingClock.Seek(value);
|
||||
}
|
||||
|
||||
private class TransformSeek : Transform<double, EditorClock>
|
||||
{
|
||||
public override string TargetMember => nameof(currentTime);
|
||||
|
||||
protected override void Apply(EditorClock clock, double time) =>
|
||||
clock.currentTime = Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
|
||||
|
||||
protected override void ReadIntoStartValue(EditorClock clock) => StartValue = clock.currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
@ -23,7 +22,7 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
private Bindable<ControlPointGroup> selectedGroup = new Bindable<ControlPointGroup>();
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock clock { get; set; }
|
||||
private EditorClock clock { get; set; }
|
||||
|
||||
protected override Drawable CreateMainContent() => new GridContainer
|
||||
{
|
||||
@ -50,7 +49,7 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
selectedGroup.BindValueChanged(selected =>
|
||||
{
|
||||
if (selected.NewValue != null)
|
||||
clock.Seek(selected.NewValue.Time);
|
||||
clock.SeekTo(selected.NewValue.Time);
|
||||
});
|
||||
}
|
||||
|
||||
@ -62,7 +61,7 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
private IBindableList<ControlPointGroup> controlGroups;
|
||||
|
||||
[Resolved]
|
||||
private IFrameBasedClock clock { get; set; }
|
||||
private EditorClock clock { get; set; }
|
||||
|
||||
[Resolved]
|
||||
protected IBindable<WorkingBeatmap> Beatmap { get; private set; }
|
||||
|
@ -32,14 +32,14 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
Text = "Start";
|
||||
}
|
||||
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> managerAdded;
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> managerRemoved;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
managerAdded = beatmaps.ItemAdded.GetBoundCopy();
|
||||
managerAdded.BindValueChanged(beatmapAdded);
|
||||
managerUpdated = beatmaps.ItemUpdated.GetBoundCopy();
|
||||
managerUpdated.BindValueChanged(beatmapUpdated);
|
||||
managerRemoved = beatmaps.ItemRemoved.GetBoundCopy();
|
||||
managerRemoved.BindValueChanged(beatmapRemoved);
|
||||
|
||||
@ -61,7 +61,7 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId) != null;
|
||||
}
|
||||
|
||||
private void beatmapAdded(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
|
||||
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
|
||||
{
|
||||
if (weakSet.NewValue.TryGetTarget(out var set))
|
||||
{
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Screens.Multi.Match
|
||||
private LeaderboardChatDisplay leaderboardChatDisplay;
|
||||
private MatchSettingsOverlay settingsOverlay;
|
||||
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> managerAdded;
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
|
||||
|
||||
public MatchSubScreen(Room room)
|
||||
{
|
||||
@ -183,8 +183,8 @@ namespace osu.Game.Screens.Multi.Match
|
||||
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged));
|
||||
SelectedItem.Value = playlist.FirstOrDefault();
|
||||
|
||||
managerAdded = beatmapManager.ItemAdded.GetBoundCopy();
|
||||
managerAdded.BindValueChanged(beatmapAdded);
|
||||
managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy();
|
||||
managerUpdated.BindValueChanged(beatmapUpdated);
|
||||
}
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
@ -217,7 +217,7 @@ namespace osu.Game.Screens.Multi.Match
|
||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
||||
}
|
||||
|
||||
private void beatmapAdded(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
|
||||
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
|
@ -131,7 +131,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private CarouselRoot root;
|
||||
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> itemAdded;
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> itemUpdated;
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> itemRemoved;
|
||||
private IBindable<WeakReference<BeatmapInfo>> itemHidden;
|
||||
private IBindable<WeakReference<BeatmapInfo>> itemRestored;
|
||||
@ -166,8 +166,8 @@ namespace osu.Game.Screens.Select
|
||||
RightClickScrollingEnabled.ValueChanged += enabled => scroll.RightMouseScrollbar = enabled.NewValue;
|
||||
RightClickScrollingEnabled.TriggerChange();
|
||||
|
||||
itemAdded = beatmaps.ItemAdded.GetBoundCopy();
|
||||
itemAdded.BindValueChanged(beatmapAdded);
|
||||
itemUpdated = beatmaps.ItemUpdated.GetBoundCopy();
|
||||
itemUpdated.BindValueChanged(beatmapUpdated);
|
||||
itemRemoved = beatmaps.ItemRemoved.GetBoundCopy();
|
||||
itemRemoved.BindValueChanged(beatmapRemoved);
|
||||
itemHidden = beatmaps.BeatmapHidden.GetBoundCopy();
|
||||
@ -582,7 +582,7 @@ namespace osu.Game.Screens.Select
|
||||
RemoveBeatmapSet(item);
|
||||
}
|
||||
|
||||
private void beatmapAdded(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakItem)
|
||||
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakItem)
|
||||
{
|
||||
if (weakItem.NewValue.TryGetTarget(out var item))
|
||||
UpdateBeatmapSet(item);
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
private IBindable<WeakReference<ScoreInfo>> itemAdded;
|
||||
private IBindable<WeakReference<ScoreInfo>> itemUpdated;
|
||||
private IBindable<WeakReference<ScoreInfo>> itemRemoved;
|
||||
|
||||
public TopLocalRank(BeatmapInfo beatmap)
|
||||
@ -40,8 +40,8 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
itemAdded = scores.ItemAdded.GetBoundCopy();
|
||||
itemAdded.BindValueChanged(scoreChanged);
|
||||
itemUpdated = scores.ItemUpdated.GetBoundCopy();
|
||||
itemUpdated.BindValueChanged(scoreChanged);
|
||||
|
||||
itemRemoved = scores.ItemRemoved.GetBoundCopy();
|
||||
itemRemoved.BindValueChanged(scoreChanged);
|
||||
|
@ -24,8 +24,6 @@ namespace osu.Game.Skinning
|
||||
|
||||
public bool DeletePending { get; set; }
|
||||
|
||||
public string FullName => $"\"{Name}\" by {Creator}";
|
||||
|
||||
public static SkinInfo Default { get; } = new SkinInfo
|
||||
{
|
||||
Name = "osu!lazer",
|
||||
@ -34,6 +32,10 @@ namespace osu.Game.Skinning
|
||||
|
||||
public bool Equals(SkinInfo other) => other != null && ID == other.ID;
|
||||
|
||||
public override string ToString() => FullName;
|
||||
public override string ToString()
|
||||
{
|
||||
string author = Creator == null ? string.Empty : $"({Creator})";
|
||||
return $"{Name} {author}".Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,7 @@ namespace osu.Game.Tests.Visual
|
||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
|
||||
dependencies.Cache(BeatDivisor);
|
||||
dependencies.CacheAs<IFrameBasedClock>(Clock);
|
||||
dependencies.CacheAs<IAdjustableClock>(Clock);
|
||||
dependencies.CacheAs(Clock);
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Compose;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
@ -32,7 +33,7 @@ namespace osu.Game.Tests.Visual
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
dependencies.CacheAs<IAdjustableClock>(new StopwatchClock());
|
||||
dependencies.CacheAs(new EditorClock());
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
@ -19,15 +19,15 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Dapper" Version="2.0.35" />
|
||||
<PackageReference Include="DiffPlex" Version="1.6.1" />
|
||||
<PackageReference Include="DiffPlex" Version="1.6.2" />
|
||||
<PackageReference Include="Humanizer" Version="2.8.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.519.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.528.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.512.0" />
|
||||
<PackageReference Include="Sentry" Version="2.1.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.1" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
@ -70,18 +70,18 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.519.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.528.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.512.0" />
|
||||
</ItemGroup>
|
||||
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<PackageReference Include="DiffPlex" Version="1.6.1" />
|
||||
<PackageReference Include="DiffPlex" Version="1.6.2" />
|
||||
<PackageReference Include="Humanizer" Version="2.8.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.519.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.528.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.1" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user