mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 10:52:53 +08:00
Merge branch 'master' into fix-combo-colours
This commit is contained in:
commit
cdc61d5f0d
@ -1 +1 @@
|
|||||||
Subproject commit d29c8365ba3cf7924b57cf22341f4af55658764c
|
Subproject commit cd6f6e93c958e3e4e98db08dd7a443cabcf4742f
|
@ -1 +1 @@
|
|||||||
Subproject commit 92ec3d10b12c5e9bfc1d3b05d3db174a506efd6d
|
Subproject commit 7bb0782200abadf73b79ed1a3bc1d5b926c6a81e
|
@ -1,26 +0,0 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection
|
|
||||||
{
|
|
||||||
public class OsuHitObjectOverlayLayer : HitObjectOverlayLayer
|
|
||||||
{
|
|
||||||
protected override HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject)
|
|
||||||
{
|
|
||||||
switch (hitObject)
|
|
||||||
{
|
|
||||||
case DrawableHitCircle circle:
|
|
||||||
return new HitCircleOverlay(circle);
|
|
||||||
case DrawableSlider slider:
|
|
||||||
return new SliderOverlay(slider);
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.CreateOverlayFor(hitObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,15 +4,15 @@
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||||
{
|
{
|
||||||
public class HitCircleOverlay : HitObjectOverlay
|
public class HitCircleMask : HitObjectMask
|
||||||
{
|
{
|
||||||
public HitCircleOverlay(DrawableHitCircle hitCircle)
|
public HitCircleMask(DrawableHitCircle hitCircle)
|
||||||
: base(hitCircle)
|
: base(hitCircle)
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
|||||||
Scale = hitCircle.Scale;
|
Scale = hitCircle.Scale;
|
||||||
|
|
||||||
AddInternal(new RingPiece());
|
AddInternal(new RingPiece());
|
||||||
|
|
||||||
|
hitCircle.HitObject.PositionChanged += _ => Position = hitCircle.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
@ -4,7 +4,7 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||||
@ -12,21 +12,21 @@ using OpenTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||||
{
|
{
|
||||||
public class SliderCircleOverlay : HitObjectOverlay
|
public class SliderCircleMask : HitObjectMask
|
||||||
{
|
{
|
||||||
public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider)
|
public SliderCircleMask(DrawableHitCircle sliderHead, DrawableSlider slider)
|
||||||
: this(sliderHead, Vector2.Zero, slider)
|
: this(sliderHead, Vector2.Zero, slider)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public SliderCircleOverlay(DrawableSliderTail sliderTail, DrawableSlider slider)
|
public SliderCircleMask(DrawableSliderTail sliderTail, DrawableSlider slider)
|
||||||
: this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1), slider)
|
: this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1), slider)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly DrawableOsuHitObject hitObject;
|
private readonly DrawableOsuHitObject hitObject;
|
||||||
|
|
||||||
private SliderCircleOverlay(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider)
|
private SliderCircleMask(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
this.hitObject = hitObject;
|
this.hitObject = hitObject;
|
@ -4,36 +4,41 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||||
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||||
{
|
{
|
||||||
public class SliderOverlay : HitObjectOverlay
|
public class SliderMask : HitObjectMask
|
||||||
{
|
{
|
||||||
private readonly SliderBody body;
|
private readonly SliderBody body;
|
||||||
private readonly DrawableSlider slider;
|
private readonly DrawableSlider slider;
|
||||||
|
|
||||||
public SliderOverlay(DrawableSlider slider)
|
public SliderMask(DrawableSlider slider)
|
||||||
: base(slider)
|
: base(slider)
|
||||||
{
|
{
|
||||||
this.slider = slider;
|
this.slider = slider;
|
||||||
|
|
||||||
var obj = (Slider)slider.HitObject;
|
Position = slider.Position;
|
||||||
|
|
||||||
|
var sliderObject = (Slider)slider.HitObject;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
body = new SliderBody(obj)
|
body = new SliderBody(sliderObject)
|
||||||
{
|
{
|
||||||
AccentColour = Color4.Transparent,
|
AccentColour = Color4.Transparent,
|
||||||
PathWidth = obj.Scale * 64
|
PathWidth = sliderObject.Scale * 64
|
||||||
},
|
},
|
||||||
new SliderCircleOverlay(slider.HeadCircle, slider),
|
new SliderCircleMask(slider.HeadCircle, slider),
|
||||||
new SliderCircleOverlay(slider.TailCircle, slider),
|
new SliderCircleMask(slider.TailCircle, slider),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sliderObject.PositionChanged += _ => Position = slider.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -46,12 +51,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
Position = slider.Position;
|
|
||||||
Size = slider.Size;
|
Size = slider.Size;
|
||||||
OriginPosition = slider.OriginPosition;
|
OriginPosition = slider.OriginPosition;
|
||||||
|
|
||||||
// Need to cause one update
|
// Need to cause one update
|
||||||
body.UpdateProgress(0);
|
body.UpdateProgress(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,10 +5,11 @@ using System.Collections.Generic;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
|
||||||
using osu.Game.Rulesets.Edit.Tools;
|
using osu.Game.Rulesets.Edit.Tools;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Layers.Selection;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
@ -32,6 +33,17 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both };
|
protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
protected override HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new OsuHitObjectOverlayLayer();
|
public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
|
||||||
|
{
|
||||||
|
switch (hitObject)
|
||||||
|
{
|
||||||
|
case DrawableHitCircle circle:
|
||||||
|
return new HitCircleMask(circle);
|
||||||
|
case DrawableSlider slider:
|
||||||
|
return new SliderMask(slider);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.CreateMaskFor(hitObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
//may not be so correct
|
//may not be so correct
|
||||||
Size = circle.DrawSize;
|
Size = circle.DrawSize;
|
||||||
|
|
||||||
|
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||||
|
@ -83,6 +83,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
components.Add(drawableRepeatPoint);
|
components.Add(drawableRepeatPoint);
|
||||||
AddNested(drawableRepeatPoint);
|
AddNested(drawableRepeatPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -1,23 +1,41 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Edit.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects
|
namespace osu.Game.Rulesets.Osu.Objects
|
||||||
{
|
{
|
||||||
public abstract class OsuHitObject : HitObject, IHasCombo, IHasPosition
|
public abstract class OsuHitObject : HitObject, IHasCombo, IHasEditablePosition
|
||||||
{
|
{
|
||||||
public const double OBJECT_RADIUS = 64;
|
public const double OBJECT_RADIUS = 64;
|
||||||
|
|
||||||
|
public event Action<Vector2> PositionChanged;
|
||||||
|
|
||||||
public double TimePreempt = 600;
|
public double TimePreempt = 600;
|
||||||
public double TimeFadein = 400;
|
public double TimeFadein = 400;
|
||||||
|
|
||||||
public Vector2 Position { get; set; }
|
private Vector2 position;
|
||||||
|
|
||||||
|
public Vector2 Position
|
||||||
|
{
|
||||||
|
get => position;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (position == value)
|
||||||
|
return;
|
||||||
|
position = value;
|
||||||
|
|
||||||
|
PositionChanged?.Invoke(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public float X => Position.X;
|
public float X => Position.X;
|
||||||
public float Y => Position.Y;
|
public float Y => Position.Y;
|
||||||
|
|
||||||
@ -48,5 +66,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void OffsetPosition(Vector2 offset) => Position += offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
private void createSliderEnds()
|
private void createSliderEnds()
|
||||||
{
|
{
|
||||||
HeadCircle = new HitCircle
|
HeadCircle = new SliderCircle(this)
|
||||||
{
|
{
|
||||||
StartTime = StartTime,
|
StartTime = StartTime,
|
||||||
Position = Position,
|
Position = Position,
|
||||||
@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
SampleControlPoint = SampleControlPoint
|
SampleControlPoint = SampleControlPoint
|
||||||
};
|
};
|
||||||
|
|
||||||
TailCircle = new HitCircle
|
TailCircle = new SliderCircle(this)
|
||||||
{
|
{
|
||||||
StartTime = EndTime,
|
StartTime = EndTime,
|
||||||
Position = EndPosition,
|
Position = EndPosition,
|
||||||
|
19
osu.Game.Rulesets.Osu/Objects/SliderCircle.cs
Normal file
19
osu.Game.Rulesets.Osu/Objects/SliderCircle.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Objects
|
||||||
|
{
|
||||||
|
public class SliderCircle : HitCircle
|
||||||
|
{
|
||||||
|
private readonly Slider slider;
|
||||||
|
|
||||||
|
public SliderCircle(Slider slider)
|
||||||
|
{
|
||||||
|
this.slider = slider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OffsetPosition(Vector2 offset) => slider.OffsetPosition(offset);
|
||||||
|
}
|
||||||
|
}
|
33
osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs
Normal file
33
osu.Game.Rulesets.Osu/Tests/TestCaseGameplayCursor.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Game.Graphics.Cursor;
|
||||||
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestCaseGameplayCursor : OsuTestCase, IProvideCursor
|
||||||
|
{
|
||||||
|
private GameplayCursor cursor;
|
||||||
|
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new [] { typeof(CursorTrail) };
|
||||||
|
|
||||||
|
public CursorContainer Cursor => cursor;
|
||||||
|
|
||||||
|
public bool ProvidingUserCursor => true;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Add(cursor = new GameplayCursor { RelativeSizeAxes = Axes.Both });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
|
||||||
using osu.Framework.Graphics.OpenGL.Buffers;
|
using osu.Framework.Graphics.OpenGL.Buffers;
|
||||||
using osu.Framework.Graphics.OpenGL.Vertices;
|
using osu.Framework.Graphics.OpenGL.Vertices;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
@ -14,11 +14,12 @@ using osu.Framework.Graphics.Textures;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
using OpenTK.Graphics.ES30;
|
using OpenTK.Graphics.ES30;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||||
{
|
{
|
||||||
internal class CursorTrail : Drawable
|
internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition
|
||||||
{
|
{
|
||||||
private int currentIndex;
|
private int currentIndex;
|
||||||
|
|
||||||
@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
private float time;
|
private float time;
|
||||||
|
|
||||||
|
public override bool IsPresent => true;
|
||||||
|
|
||||||
private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData();
|
private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData();
|
||||||
private const int max_sprites = 2048;
|
private const int max_sprites = 2048;
|
||||||
|
|
||||||
@ -96,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
const int fade_clock_reset_threshold = 1000000;
|
const int fade_clock_reset_threshold = 1000000;
|
||||||
|
|
||||||
time = (float)(Time.Current - timeOffset) / 500f;
|
time = (float)(Time.Current - timeOffset) / 300f;
|
||||||
if (time > fade_clock_reset_threshold)
|
if (time > fade_clock_reset_threshold)
|
||||||
resetTime();
|
resetTime();
|
||||||
}
|
}
|
||||||
@ -115,14 +118,16 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
protected override bool OnMouseMove(InputState state)
|
protected override bool OnMouseMove(InputState state)
|
||||||
{
|
{
|
||||||
|
Vector2 pos = state.Mouse.NativeState.Position;
|
||||||
|
|
||||||
if (lastPosition == null)
|
if (lastPosition == null)
|
||||||
{
|
{
|
||||||
lastPosition = state.Mouse.NativeState.Position;
|
lastPosition = pos;
|
||||||
resampler.AddPosition(lastPosition.Value);
|
resampler.AddPosition(lastPosition.Value);
|
||||||
return base.OnMouseMove(state);
|
return base.OnMouseMove(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Vector2 pos2 in resampler.AddPosition(state.Mouse.NativeState.Position))
|
foreach (Vector2 pos2 in resampler.AddPosition(pos))
|
||||||
{
|
{
|
||||||
Trace.Assert(lastPosition.HasValue);
|
Trace.Assert(lastPosition.HasValue);
|
||||||
|
|
||||||
@ -162,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
private class TrailDrawNodeSharedData
|
private class TrailDrawNodeSharedData
|
||||||
{
|
{
|
||||||
public VertexBuffer<TexturedVertex2D> VertexBuffer;
|
public VertexBuffer<TexturedTrailVertex> VertexBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TrailDrawNode : DrawNode
|
private class TrailDrawNode : DrawNode
|
||||||
@ -188,7 +193,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
public override void Draw(Action<TexturedVertex2D> vertexAction)
|
public override void Draw(Action<TexturedVertex2D> vertexAction)
|
||||||
{
|
{
|
||||||
if (Shared.VertexBuffer == null)
|
if (Shared.VertexBuffer == null)
|
||||||
Shared.VertexBuffer = new QuadVertexBuffer<TexturedVertex2D>(max_sprites, BufferUsageHint.DynamicDraw);
|
Shared.VertexBuffer = new QuadVertexBuffer<TexturedTrailVertex>(max_sprites, BufferUsageHint.DynamicDraw);
|
||||||
|
|
||||||
Shader.GetUniform<float>("g_FadeClock").Value = Time;
|
Shader.GetUniform<float>("g_FadeClock").Value = Time;
|
||||||
|
|
||||||
@ -205,17 +210,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
int end = start;
|
int end = start;
|
||||||
|
|
||||||
Vector2 pos = Parts[i].Position;
|
Vector2 pos = Parts[i].Position;
|
||||||
ColourInfo colour = DrawInfo.Colour;
|
float time = Parts[i].Time;
|
||||||
colour.TopLeft.Linear.A = Parts[i].Time + colour.TopLeft.Linear.A;
|
|
||||||
colour.TopRight.Linear.A = Parts[i].Time + colour.TopRight.Linear.A;
|
|
||||||
colour.BottomLeft.Linear.A = Parts[i].Time + colour.BottomLeft.Linear.A;
|
|
||||||
colour.BottomRight.Linear.A = Parts[i].Time + colour.BottomRight.Linear.A;
|
|
||||||
|
|
||||||
Texture.DrawQuad(
|
Texture.DrawQuad(
|
||||||
new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y),
|
new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y),
|
||||||
colour,
|
DrawInfo.Colour,
|
||||||
null,
|
null,
|
||||||
v => Shared.VertexBuffer.Vertices[end++] = v);
|
v => Shared.VertexBuffer.Vertices[end++] = new TexturedTrailVertex
|
||||||
|
{
|
||||||
|
Position = v.Position,
|
||||||
|
TexturePosition = v.TexturePosition,
|
||||||
|
Time = time + 1,
|
||||||
|
Colour = v.Colour,
|
||||||
|
});
|
||||||
|
|
||||||
Parts[i].WasUpdated = false;
|
Parts[i].WasUpdated = false;
|
||||||
}
|
}
|
||||||
@ -240,5 +247,26 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
Shader.Unbind();
|
Shader.Unbind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct TexturedTrailVertex : IEquatable<TexturedTrailVertex>, IVertex
|
||||||
|
{
|
||||||
|
[VertexMember(2, VertexAttribPointerType.Float)]
|
||||||
|
public Vector2 Position;
|
||||||
|
[VertexMember(4, VertexAttribPointerType.Float)]
|
||||||
|
public Color4 Colour;
|
||||||
|
[VertexMember(2, VertexAttribPointerType.Float)]
|
||||||
|
public Vector2 TexturePosition;
|
||||||
|
[VertexMember(1, VertexAttribPointerType.Float)]
|
||||||
|
public float Time;
|
||||||
|
|
||||||
|
public bool Equals(TexturedTrailVertex other)
|
||||||
|
{
|
||||||
|
return Position.Equals(other.Position)
|
||||||
|
&& TexturePosition.Equals(other.TexturePosition)
|
||||||
|
&& Colour.Equals(other.Colour)
|
||||||
|
&& Time.Equals(other.Time);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,66 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
{
|
{
|
||||||
protected override Drawable CreateCursor() => new OsuCursor();
|
protected override Drawable CreateCursor() => new OsuCursor();
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => fadeContainer;
|
||||||
|
|
||||||
|
private readonly Container<Drawable> fadeContainer;
|
||||||
|
|
||||||
public GameplayCursor()
|
public GameplayCursor()
|
||||||
{
|
{
|
||||||
Add(new CursorTrail { Depth = 1 });
|
InternalChild = fadeContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new CursorTrail { Depth = 1 }
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private int downCount;
|
private int downCount;
|
||||||
|
|
||||||
|
public bool OnPressed(OsuAction action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case OsuAction.LeftButton:
|
||||||
|
case OsuAction.RightButton:
|
||||||
|
downCount++;
|
||||||
|
ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(OsuAction action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case OsuAction.LeftButton:
|
||||||
|
case OsuAction.RightButton:
|
||||||
|
if (--downCount == 0)
|
||||||
|
ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
fadeContainer.FadeTo(1, 300, Easing.OutQuint);
|
||||||
|
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
|
||||||
|
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
public class OsuCursor : Container
|
public class OsuCursor : Container
|
||||||
{
|
{
|
||||||
private Container cursorContainer;
|
private Container cursorContainer;
|
||||||
@ -131,45 +184,5 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
cursorContainer.Scale = new Vector2(scale);
|
cursorContainer.Scale = new Vector2(scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnPressed(OsuAction action)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case OsuAction.LeftButton:
|
|
||||||
case OsuAction.RightButton:
|
|
||||||
downCount++;
|
|
||||||
ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OnReleased(OsuAction action)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case OsuAction.LeftButton:
|
|
||||||
case OsuAction.RightButton:
|
|
||||||
if (--downCount == 0)
|
|
||||||
ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopIn()
|
|
||||||
{
|
|
||||||
ActiveCursor.FadeTo(1, 250, Easing.OutQuint);
|
|
||||||
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopOut()
|
|
||||||
{
|
|
||||||
ActiveCursor.FadeTo(0, 250, Easing.OutQuint);
|
|
||||||
ActiveCursor.ScaleTo(0.6f, 250, Easing.In);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,10 +64,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Beatmaps\OsuBeatmapConverter.cs" />
|
<Compile Include="Beatmaps\OsuBeatmapConverter.cs" />
|
||||||
<Compile Include="Beatmaps\OsuBeatmapProcessor.cs" />
|
<Compile Include="Beatmaps\OsuBeatmapProcessor.cs" />
|
||||||
<Compile Include="Edit\Layers\Selection\OsuHitObjectOverlayLayer.cs" />
|
<Compile Include="Edit\Layers\Selection\Overlays\HitCircleMask.cs" />
|
||||||
<Compile Include="Edit\Layers\Selection\Overlays\HitCircleOverlay.cs" />
|
<Compile Include="Edit\Layers\Selection\Overlays\SliderCircleMask.cs" />
|
||||||
<Compile Include="Edit\Layers\Selection\Overlays\SliderCircleOverlay.cs" />
|
<Compile Include="Edit\Layers\Selection\Overlays\SliderMask.cs" />
|
||||||
<Compile Include="Edit\Layers\Selection\Overlays\SliderOverlay.cs" />
|
|
||||||
<Compile Include="Edit\OsuEditPlayfield.cs" />
|
<Compile Include="Edit\OsuEditPlayfield.cs" />
|
||||||
<Compile Include="Edit\OsuEditRulesetContainer.cs" />
|
<Compile Include="Edit\OsuEditRulesetContainer.cs" />
|
||||||
<Compile Include="Edit\OsuHitObjectComposer.cs" />
|
<Compile Include="Edit\OsuHitObjectComposer.cs" />
|
||||||
@ -118,6 +117,7 @@
|
|||||||
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
|
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
|
||||||
<Compile Include="Objects\ISliderProgress.cs" />
|
<Compile Include="Objects\ISliderProgress.cs" />
|
||||||
<Compile Include="Objects\RepeatPoint.cs" />
|
<Compile Include="Objects\RepeatPoint.cs" />
|
||||||
|
<Compile Include="Objects\SliderCircle.cs" />
|
||||||
<Compile Include="Objects\SliderTick.cs" />
|
<Compile Include="Objects\SliderTick.cs" />
|
||||||
<Compile Include="OsuDifficulty\OsuDifficultyCalculator.cs" />
|
<Compile Include="OsuDifficulty\OsuDifficultyCalculator.cs" />
|
||||||
<Compile Include="OsuDifficulty\Preprocessing\OsuDifficultyBeatmap.cs" />
|
<Compile Include="OsuDifficulty\Preprocessing\OsuDifficultyBeatmap.cs" />
|
||||||
@ -131,6 +131,7 @@
|
|||||||
<Compile Include="Replays\OsuReplayInputHandler.cs" />
|
<Compile Include="Replays\OsuReplayInputHandler.cs" />
|
||||||
<Compile Include="Tests\OsuBeatmapConversionTest.cs" />
|
<Compile Include="Tests\OsuBeatmapConversionTest.cs" />
|
||||||
<Compile Include="Tests\TestCaseEditor.cs" />
|
<Compile Include="Tests\TestCaseEditor.cs" />
|
||||||
|
<Compile Include="Tests\TestCaseGameplayCursor.cs" />
|
||||||
<Compile Include="Tests\TestCaseHitCircle.cs" />
|
<Compile Include="Tests\TestCaseHitCircle.cs" />
|
||||||
<Compile Include="Tests\TestCaseHitCircleHidden.cs" />
|
<Compile Include="Tests\TestCaseHitCircleHidden.cs" />
|
||||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||||
|
@ -8,13 +8,12 @@ using osu.Framework.Allocation;
|
|||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Edit;
|
using osu.Game.Rulesets.Osu.Edit;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Layers.Selection;
|
|
||||||
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
|
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Screens.Edit.Screens.Compose.Layers;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual
|
namespace osu.Game.Tests.Visual
|
||||||
@ -24,17 +23,15 @@ namespace osu.Game.Tests.Visual
|
|||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
typeof(SelectionBox),
|
|
||||||
typeof(SelectionLayer),
|
typeof(SelectionLayer),
|
||||||
typeof(CaptureBox),
|
typeof(SelectionBox),
|
||||||
typeof(HitObjectComposer),
|
typeof(HitObjectComposer),
|
||||||
typeof(OsuHitObjectComposer),
|
typeof(OsuHitObjectComposer),
|
||||||
typeof(HitObjectOverlayLayer),
|
typeof(HitObjectMaskLayer),
|
||||||
typeof(OsuHitObjectOverlayLayer),
|
typeof(HitObjectMask),
|
||||||
typeof(HitObjectOverlay),
|
typeof(HitCircleMask),
|
||||||
typeof(HitCircleOverlay),
|
typeof(SliderMask),
|
||||||
typeof(SliderOverlay),
|
typeof(SliderCircleMask)
|
||||||
typeof(SliderCircleOverlay)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Add(new SkipButton(Clock.CurrentTime + 5000));
|
Add(new SkipOverlay(Clock.CurrentTime + 5000));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,10 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Edit.Layers;
|
|
||||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
|
||||||
using osu.Game.Rulesets.Edit.Tools;
|
using osu.Game.Rulesets.Edit.Tools;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Edit.Screens.Compose.Layers;
|
||||||
using osu.Game.Screens.Edit.Screens.Compose.RadioButtons;
|
using osu.Game.Screens.Edit.Screens.Compose.RadioButtons;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit
|
namespace osu.Game.Rulesets.Edit
|
||||||
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HitObjectOverlayLayer hitObjectOverlayLayer = CreateHitObjectOverlayLayer();
|
HitObjectMaskLayer hitObjectMaskLayer = new HitObjectMaskLayer(this);
|
||||||
SelectionLayer selectionLayer = new SelectionLayer(rulesetContainer.Playfield);
|
SelectionLayer selectionLayer = new SelectionLayer(rulesetContainer.Playfield);
|
||||||
|
|
||||||
var layerBelowRuleset = new BorderLayer
|
var layerBelowRuleset = new BorderLayer
|
||||||
@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
layerAboveRuleset.Children = new Drawable[]
|
layerAboveRuleset.Children = new Drawable[]
|
||||||
{
|
{
|
||||||
selectionLayer, // Below object overlays for input
|
selectionLayer, // Below object overlays for input
|
||||||
hitObjectOverlayLayer,
|
hitObjectMaskLayer,
|
||||||
selectionLayer.CreateProxy() // Proxy above object overlays for selections
|
selectionLayer.CreateProxy() // Proxy above object overlays for selections
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -106,8 +106,10 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
selectionLayer.ObjectSelected += hitObjectOverlayLayer.AddOverlay;
|
selectionLayer.ObjectSelected += hitObjectMaskLayer.AddOverlay;
|
||||||
selectionLayer.ObjectDeselected += hitObjectOverlayLayer.RemoveOverlay;
|
selectionLayer.ObjectDeselected += hitObjectMaskLayer.RemoveOverlay;
|
||||||
|
selectionLayer.SelectionCleared += hitObjectMaskLayer.RemoveSelectionOverlay;
|
||||||
|
selectionLayer.SelectionFinished += hitObjectMaskLayer.AddSelectionOverlay;
|
||||||
|
|
||||||
toolboxCollection.Items =
|
toolboxCollection.Items =
|
||||||
new[] { new RadioButton("Select", () => setCompositionTool(null)) }
|
new[] { new RadioButton("Select", () => setCompositionTool(null)) }
|
||||||
@ -138,14 +140,22 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
protected abstract IReadOnlyList<ICompositionTool> CompositionTools { get; }
|
protected abstract IReadOnlyList<ICompositionTool> CompositionTools { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="HitObjectMask"/> for a specific <see cref="DrawableHitObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create the overlay for.</param>
|
||||||
|
public virtual HitObjectMask CreateMaskFor(DrawableHitObject hitObject) => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="SelectionBox"/> which outlines <see cref="DrawableHitObject"/>s
|
||||||
|
/// and handles all hitobject movement/pattern adjustments.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="overlays">The <see cref="DrawableHitObject"/> overlays.</param>
|
||||||
|
public virtual SelectionBox CreateSelectionOverlay(IReadOnlyList<HitObjectMask> overlays) => new SelectionBox(overlays);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>.
|
/// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both };
|
protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates the <see cref="HitObjectOverlayLayer"/> which overlays selected <see cref="DrawableHitObject"/>s.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new HitObjectOverlayLayer();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
osu.Game/Rulesets/Edit/HitObjectMask.cs
Normal file
21
osu.Game/Rulesets/Edit/HitObjectMask.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Edit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A mask placed above a <see cref="DrawableHitObject"/> adding editing functionality.
|
||||||
|
/// </summary>
|
||||||
|
public class HitObjectMask : Container
|
||||||
|
{
|
||||||
|
public readonly DrawableHitObject HitObject;
|
||||||
|
|
||||||
|
public HitObjectMask(DrawableHitObject hitObject)
|
||||||
|
{
|
||||||
|
HitObject = hitObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,68 +0,0 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using OpenTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A box which encloses <see cref="DrawableHitObject"/>s.
|
|
||||||
/// </summary>
|
|
||||||
public class CaptureBox : VisibilityContainer
|
|
||||||
{
|
|
||||||
private readonly IDrawable captureArea;
|
|
||||||
private readonly IReadOnlyList<DrawableHitObject> capturedObjects;
|
|
||||||
|
|
||||||
public CaptureBox(IDrawable captureArea, IReadOnlyList<DrawableHitObject> capturedObjects)
|
|
||||||
{
|
|
||||||
this.captureArea = captureArea;
|
|
||||||
this.capturedObjects = capturedObjects;
|
|
||||||
|
|
||||||
Masking = true;
|
|
||||||
BorderThickness = SelectionBox.BORDER_RADIUS;
|
|
||||||
|
|
||||||
InternalChild = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
AlwaysPresent = true,
|
|
||||||
Alpha = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
State = Visibility.Visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
BorderColour = colours.Yellow;
|
|
||||||
|
|
||||||
// Move the rectangle to cover the hitobjects
|
|
||||||
var topLeft = new Vector2(float.MaxValue, float.MaxValue);
|
|
||||||
var bottomRight = new Vector2(float.MinValue, float.MinValue);
|
|
||||||
|
|
||||||
foreach (var obj in capturedObjects)
|
|
||||||
{
|
|
||||||
topLeft = Vector2.ComponentMin(topLeft, captureArea.ToLocalSpace(obj.SelectionQuad.TopLeft));
|
|
||||||
bottomRight = Vector2.ComponentMax(bottomRight, captureArea.ToLocalSpace(obj.SelectionQuad.BottomRight));
|
|
||||||
}
|
|
||||||
|
|
||||||
topLeft -= new Vector2(5);
|
|
||||||
bottomRight += new Vector2(5);
|
|
||||||
|
|
||||||
Size = bottomRight - topLeft;
|
|
||||||
Position = topLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool DisposeOnDeathRemoval => true;
|
|
||||||
|
|
||||||
protected override void PopIn() => this.FadeIn();
|
|
||||||
protected override void PopOut() => this.FadeOut();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|
||||||
{
|
|
||||||
public class HitObjectOverlay : OverlayContainer
|
|
||||||
{
|
|
||||||
// ReSharper disable once NotAccessedField.Local
|
|
||||||
// This will be used later to handle drag movement, etc
|
|
||||||
private readonly DrawableHitObject hitObject;
|
|
||||||
|
|
||||||
public HitObjectOverlay(DrawableHitObject hitObject)
|
|
||||||
{
|
|
||||||
this.hitObject = hitObject;
|
|
||||||
|
|
||||||
State = Visibility.Visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopIn() => Alpha = 1;
|
|
||||||
protected override void PopOut() => Alpha = 0;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Primitives;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A box that represents a drag selection.
|
|
||||||
/// </summary>
|
|
||||||
public class SelectionBox : VisibilityContainer
|
|
||||||
{
|
|
||||||
public const float BORDER_RADIUS = 2;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="SelectionBox"/>.
|
|
||||||
/// </summary>
|
|
||||||
public SelectionBox()
|
|
||||||
{
|
|
||||||
Masking = true;
|
|
||||||
BorderColour = Color4.White;
|
|
||||||
BorderThickness = BORDER_RADIUS;
|
|
||||||
|
|
||||||
Child = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0.1f
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDragRectangle(RectangleF rectangle)
|
|
||||||
{
|
|
||||||
var topLeft = Parent.ToLocalSpace(rectangle.TopLeft);
|
|
||||||
var bottomRight = Parent.ToLocalSpace(rectangle.BottomRight);
|
|
||||||
|
|
||||||
Position = topLeft;
|
|
||||||
Size = bottomRight - topLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool DisposeOnDeathRemoval => true;
|
|
||||||
|
|
||||||
protected override void PopIn() => this.FadeIn(250, Easing.OutQuint);
|
|
||||||
protected override void PopOut() => this.FadeOut(250, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
}
|
|
13
osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs
Normal file
13
osu.Game/Rulesets/Edit/Types/IHasEditablePosition.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Edit.Types
|
||||||
|
{
|
||||||
|
public interface IHasEditablePosition : IHasPosition
|
||||||
|
{
|
||||||
|
void OffsetPosition(Vector2 offset);
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit.Layers
|
namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||||
{
|
{
|
||||||
public class BorderLayer : Container
|
public class BorderLayer : Container
|
||||||
{
|
{
|
@ -1,20 +1,25 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit.Layers.Selection
|
namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||||
{
|
{
|
||||||
public class HitObjectOverlayLayer : CompositeDrawable
|
public class HitObjectMaskLayer : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly Dictionary<DrawableHitObject, HitObjectOverlay> existingOverlays = new Dictionary<DrawableHitObject, HitObjectOverlay>();
|
private readonly HitObjectComposer composer;
|
||||||
|
private readonly Container<HitObjectMask> overlayContainer;
|
||||||
|
|
||||||
public HitObjectOverlayLayer()
|
public HitObjectMaskLayer(HitObjectComposer composer)
|
||||||
{
|
{
|
||||||
|
this.composer = composer;
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = overlayContainer = new Container<HitObjectMask> { RelativeSizeAxes = Axes.Both };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -23,12 +28,11 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|||||||
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create an overlay for.</param>
|
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create an overlay for.</param>
|
||||||
public void AddOverlay(DrawableHitObject hitObject)
|
public void AddOverlay(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
var overlay = CreateOverlayFor(hitObject);
|
var overlay = composer.CreateMaskFor(hitObject);
|
||||||
if (overlay == null)
|
if (overlay == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
existingOverlays[hitObject] = overlay;
|
overlayContainer.Add(overlay);
|
||||||
AddInternal(overlay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -37,17 +41,22 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|||||||
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to remove the overlay for.</param>
|
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to remove the overlay for.</param>
|
||||||
public void RemoveOverlay(DrawableHitObject hitObject)
|
public void RemoveOverlay(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
if (!existingOverlays.TryGetValue(hitObject, out var existing))
|
var existing = overlayContainer.FirstOrDefault(h => h.HitObject == hitObject);
|
||||||
|
if (existing == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
existing.Hide();
|
existing.Hide();
|
||||||
existing.Expire();
|
existing.Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private SelectionBox currentSelectionBox;
|
||||||
/// Creates a <see cref="HitObjectOverlay"/> for a specific <see cref="DrawableHitObject"/>.
|
|
||||||
/// </summary>
|
public void AddSelectionOverlay() => AddInternal(currentSelectionBox = composer.CreateSelectionOverlay(overlayContainer));
|
||||||
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create the overlay for.</param>
|
|
||||||
protected virtual HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject) => null;
|
public void RemoveSelectionOverlay()
|
||||||
|
{
|
||||||
|
currentSelectionBox?.Hide();
|
||||||
|
currentSelectionBox?.Expire();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
102
osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs
Normal file
102
osu.Game/Screens/Edit/Screens/Compose/Layers/SelectionBox.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Edit.Types;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A box which surrounds <see cref="DrawableHitObject"/>s and provides interactive handles, context menus etc.
|
||||||
|
/// </summary>
|
||||||
|
public class SelectionBox : VisibilityContainer
|
||||||
|
{
|
||||||
|
private readonly IReadOnlyList<HitObjectMask> overlays;
|
||||||
|
|
||||||
|
public const float BORDER_RADIUS = 2;
|
||||||
|
|
||||||
|
public SelectionBox(IReadOnlyList<HitObjectMask> overlays)
|
||||||
|
{
|
||||||
|
this.overlays = overlays;
|
||||||
|
|
||||||
|
Masking = true;
|
||||||
|
BorderThickness = BORDER_RADIUS;
|
||||||
|
|
||||||
|
InternalChild = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
AlwaysPresent = true,
|
||||||
|
Alpha = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
State = Visibility.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
BorderColour = colours.Yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
// Todo: We might need to optimise this
|
||||||
|
|
||||||
|
// Move the rectangle to cover the hitobjects
|
||||||
|
var topLeft = new Vector2(float.MaxValue, float.MaxValue);
|
||||||
|
var bottomRight = new Vector2(float.MinValue, float.MinValue);
|
||||||
|
|
||||||
|
foreach (var obj in overlays)
|
||||||
|
{
|
||||||
|
topLeft = Vector2.ComponentMin(topLeft, Parent.ToLocalSpace(obj.HitObject.SelectionQuad.TopLeft));
|
||||||
|
bottomRight = Vector2.ComponentMax(bottomRight, Parent.ToLocalSpace(obj.HitObject.SelectionQuad.BottomRight));
|
||||||
|
}
|
||||||
|
|
||||||
|
topLeft -= new Vector2(5);
|
||||||
|
bottomRight += new Vector2(5);
|
||||||
|
|
||||||
|
Size = bottomRight - topLeft;
|
||||||
|
Position = topLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => overlays.Any(o => o.ReceiveMouseInputAt(screenSpacePos));
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
|
||||||
|
|
||||||
|
protected override bool OnDragStart(InputState state) => true;
|
||||||
|
|
||||||
|
protected override bool OnDrag(InputState state)
|
||||||
|
{
|
||||||
|
// Todo: Various forms of snapping
|
||||||
|
foreach (var hitObject in overlays.Select(o => o.HitObject.HitObject))
|
||||||
|
{
|
||||||
|
switch (hitObject)
|
||||||
|
{
|
||||||
|
case IHasEditablePosition editablePosition:
|
||||||
|
editablePosition.OffsetPosition(state.Mouse.Delta);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnDragEnd(InputState state) => true;
|
||||||
|
|
||||||
|
public override bool DisposeOnDeathRemoval => true;
|
||||||
|
|
||||||
|
protected override void PopIn() => this.FadeIn();
|
||||||
|
protected override void PopOut() => this.FadeOut();
|
||||||
|
}
|
||||||
|
}
|
@ -8,12 +8,14 @@ using osu.Framework.Extensions.IEnumerableExtensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit.Layers.Selection
|
namespace osu.Game.Screens.Edit.Screens.Compose.Layers
|
||||||
{
|
{
|
||||||
public class SelectionLayer : CompositeDrawable
|
public class SelectionLayer : CompositeDrawable
|
||||||
{
|
{
|
||||||
@ -27,6 +29,16 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<DrawableHitObject> ObjectDeselected;
|
public event Action<DrawableHitObject> ObjectDeselected;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when the selection has been cleared.
|
||||||
|
/// </summary>
|
||||||
|
public event Action SelectionCleared;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when the user has finished selecting all <see cref="DrawableHitObject"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public event Action SelectionFinished;
|
||||||
|
|
||||||
private readonly Playfield playfield;
|
private readonly Playfield playfield;
|
||||||
|
|
||||||
public SelectionLayer(Playfield playfield)
|
public SelectionLayer(Playfield playfield)
|
||||||
@ -36,8 +48,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SelectionBox selectionBox;
|
private DragBox dragBox;
|
||||||
private CaptureBox captureBox;
|
|
||||||
|
|
||||||
private readonly HashSet<DrawableHitObject> selectedHitObjects = new HashSet<DrawableHitObject>();
|
private readonly HashSet<DrawableHitObject> selectedHitObjects = new HashSet<DrawableHitObject>();
|
||||||
|
|
||||||
@ -49,20 +60,20 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|||||||
|
|
||||||
protected override bool OnDragStart(InputState state)
|
protected override bool OnDragStart(InputState state)
|
||||||
{
|
{
|
||||||
AddInternal(selectionBox = new SelectionBox());
|
AddInternal(dragBox = new DragBox());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnDrag(InputState state)
|
protected override bool OnDrag(InputState state)
|
||||||
{
|
{
|
||||||
selectionBox.Show();
|
dragBox.Show();
|
||||||
|
|
||||||
var dragPosition = state.Mouse.NativeState.Position;
|
var dragPosition = state.Mouse.NativeState.Position;
|
||||||
var dragStartPosition = state.Mouse.NativeState.PositionMouseDown ?? dragPosition;
|
var dragStartPosition = state.Mouse.NativeState.PositionMouseDown ?? dragPosition;
|
||||||
|
|
||||||
var screenSpaceDragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y);
|
var screenSpaceDragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y);
|
||||||
|
|
||||||
selectionBox.SetDragRectangle(screenSpaceDragQuad.AABBFloat);
|
dragBox.SetDragRectangle(screenSpaceDragQuad.AABBFloat);
|
||||||
selectQuad(screenSpaceDragQuad);
|
selectQuad(screenSpaceDragQuad);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -70,8 +81,8 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|||||||
|
|
||||||
protected override bool OnDragEnd(InputState state)
|
protected override bool OnDragEnd(InputState state)
|
||||||
{
|
{
|
||||||
selectionBox.Hide();
|
dragBox.Hide();
|
||||||
selectionBox.Expire();
|
dragBox.Expire();
|
||||||
|
|
||||||
finishSelection();
|
finishSelection();
|
||||||
|
|
||||||
@ -95,7 +106,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|||||||
if (!select(hitObject))
|
if (!select(hitObject))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clearCapture();
|
clearSelection();
|
||||||
finishSelection();
|
finishSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +133,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|||||||
if (!deselect(hitObject))
|
if (!deselect(hitObject))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clearCapture();
|
clearSelection();
|
||||||
finishSelection();
|
finishSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +159,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|||||||
selectedHitObjects.ForEach(h => ObjectDeselected?.Invoke(h));
|
selectedHitObjects.ForEach(h => ObjectDeselected?.Invoke(h));
|
||||||
selectedHitObjects.Clear();
|
selectedHitObjects.Clear();
|
||||||
|
|
||||||
clearCapture();
|
clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -180,18 +191,49 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
|
|||||||
select(target);
|
select(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearCapture()
|
private void clearSelection() => SelectionCleared?.Invoke();
|
||||||
{
|
|
||||||
captureBox?.Hide();
|
|
||||||
captureBox?.Expire();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void finishSelection()
|
private void finishSelection()
|
||||||
{
|
{
|
||||||
if (selectedHitObjects.Count == 0)
|
if (selectedHitObjects.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
SelectionFinished?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
AddInternal(captureBox = new CaptureBox(this, selectedHitObjects.ToList()));
|
/// <summary>
|
||||||
|
/// A box that represents a drag selection.
|
||||||
|
/// </summary>
|
||||||
|
private class DragBox : VisibilityContainer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="DragBox"/>.
|
||||||
|
/// </summary>
|
||||||
|
public DragBox()
|
||||||
|
{
|
||||||
|
Masking = true;
|
||||||
|
BorderColour = Color4.White;
|
||||||
|
BorderThickness = SelectionBox.BORDER_RADIUS;
|
||||||
|
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0.1f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDragRectangle(RectangleF rectangle)
|
||||||
|
{
|
||||||
|
var topLeft = Parent.ToLocalSpace(rectangle.TopLeft);
|
||||||
|
var bottomRight = Parent.ToLocalSpace(rectangle.BottomRight);
|
||||||
|
|
||||||
|
Position = topLeft;
|
||||||
|
Size = bottomRight - topLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool DisposeOnDeathRemoval => true;
|
||||||
|
|
||||||
|
protected override void PopIn() => this.FadeIn(250, Easing.OutQuint);
|
||||||
|
protected override void PopOut() => this.FadeOut(250, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,6 +8,9 @@ using osu.Game.Beatmaps;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Screens
|
namespace osu.Game.Screens.Edit.Screens
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// TODO: eventually make this inherit Screen and add a local scren stack inside the Editor.
|
||||||
|
/// </summary>
|
||||||
public class EditorScreen : Container
|
public class EditorScreen : Container
|
||||||
{
|
{
|
||||||
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||||
|
@ -18,7 +18,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
public abstract class GameplayMenuOverlay : OverlayContainer, IRequireHighFrequencyMousePosition
|
public abstract class GameplayMenuOverlay : OverlayContainer
|
||||||
{
|
{
|
||||||
private const int transition_duration = 200;
|
private const int transition_duration = 200;
|
||||||
private const int button_height = 70;
|
private const int button_height = 70;
|
||||||
|
@ -164,7 +164,7 @@ namespace osu.Game.Screens.Play
|
|||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
},
|
},
|
||||||
RulesetContainer,
|
RulesetContainer,
|
||||||
new SkipButton(firstObjectTime)
|
new SkipOverlay(firstObjectTime)
|
||||||
{
|
{
|
||||||
Clock = Clock, // skip button doesn't want to use the audio clock directly
|
Clock = Clock, // skip button doesn't want to use the audio clock directly
|
||||||
ProcessCustomClock = false,
|
ProcessCustomClock = false,
|
||||||
|
@ -5,12 +5,14 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Screens.Play.PlayerSettings;
|
using osu.Game.Screens.Play.PlayerSettings;
|
||||||
|
|
||||||
@ -21,7 +23,6 @@ namespace osu.Game.Screens.Play
|
|||||||
private Player player;
|
private Player player;
|
||||||
|
|
||||||
private BeatmapMetadataDisplay info;
|
private BeatmapMetadataDisplay info;
|
||||||
private VisualSettings visualSettings;
|
|
||||||
|
|
||||||
private bool showOverlays = true;
|
private bool showOverlays = true;
|
||||||
public override bool ShowOverlaysOnEnter => showOverlays;
|
public override bool ShowOverlaysOnEnter => showOverlays;
|
||||||
@ -46,7 +47,8 @@ namespace osu.Game.Screens.Play
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
});
|
});
|
||||||
Add(visualSettings = new VisualSettings
|
|
||||||
|
Add(new VisualSettings
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
@ -93,7 +95,7 @@ namespace osu.Game.Screens.Play
|
|||||||
contentIn();
|
contentIn();
|
||||||
|
|
||||||
info.Delay(750).FadeIn(500);
|
info.Delay(750).FadeIn(500);
|
||||||
this.Delay(2150).Schedule(pushWhenLoaded);
|
this.Delay(1800).Schedule(pushWhenLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
||||||
@ -109,14 +111,44 @@ namespace osu.Game.Screens.Play
|
|||||||
logo.Delay(resuming ? 0 : 500).MoveToOffset(new Vector2(0, -0.24f), 500, Easing.InOutExpo);
|
logo.Delay(resuming ? 0 : 500).MoveToOffset(new Vector2(0, -0.24f), 500, Easing.InOutExpo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool weHandledMouseDown;
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||||
|
{
|
||||||
|
weHandledMouseDown = true;
|
||||||
|
return base.OnMouseDown(state, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||||
|
{
|
||||||
|
weHandledMouseDown = false;
|
||||||
|
return base.OnMouseUp(state, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScheduledDelegate pushDebounce;
|
||||||
|
|
||||||
|
private bool readyForPush => player.LoadState == LoadState.Ready && IsHovered && (!GetContainingInputManager().CurrentState.Mouse.HasAnyButtonPressed || weHandledMouseDown);
|
||||||
|
|
||||||
private void pushWhenLoaded()
|
private void pushWhenLoaded()
|
||||||
{
|
{
|
||||||
if (player.LoadState != LoadState.Ready || visualSettings.IsHovered)
|
if (!IsCurrentScreen) return;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Schedule(pushWhenLoaded);
|
if (!readyForPush)
|
||||||
|
{
|
||||||
|
// as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
|
||||||
|
// if we become unready for push during the delay.
|
||||||
|
pushDebounce?.Cancel();
|
||||||
|
pushDebounce = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pushDebounce != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pushDebounce = Scheduler.AddDelayed(() =>
|
||||||
|
{
|
||||||
contentOut();
|
contentOut();
|
||||||
|
|
||||||
this.Delay(250).Schedule(() =>
|
this.Delay(250).Schedule(() =>
|
||||||
@ -132,6 +164,12 @@ namespace osu.Game.Screens.Play
|
|||||||
ValidForResume = false;
|
ValidForResume = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Schedule(pushWhenLoaded);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnExiting(Screen next)
|
protected override bool OnExiting(Screen next)
|
||||||
|
@ -5,6 +5,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -133,5 +134,8 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
|
protected override bool OnHover(InputState state) => true;
|
||||||
|
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ using osu.Game.Input.Bindings;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
public class SkipButton : OverlayContainer, IKeyBindingHandler<GlobalAction>
|
public class SkipOverlay : OverlayContainer, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
private readonly double startTime;
|
private readonly double startTime;
|
||||||
|
|
||||||
@ -35,8 +35,9 @@ namespace osu.Game.Screens.Play
|
|||||||
private double displayTime;
|
private double displayTime;
|
||||||
|
|
||||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
|
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
|
||||||
|
protected override bool BlockPassThroughMouse => false;
|
||||||
|
|
||||||
public SkipButton(double startTime)
|
public SkipOverlay(double startTime)
|
||||||
{
|
{
|
||||||
this.startTime = startTime;
|
this.startTime = startTime;
|
||||||
|
|
||||||
@ -51,12 +52,6 @@ namespace osu.Game.Screens.Play
|
|||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseMove(InputState state)
|
|
||||||
{
|
|
||||||
fadeContainer.State = Visibility.Visible;
|
|
||||||
return base.OnMouseMove(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
@ -121,15 +116,9 @@ namespace osu.Game.Screens.Play
|
|||||||
Expire();
|
Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn() => this.FadeIn();
|
||||||
{
|
|
||||||
this.FadeIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut() => this.FadeOut();
|
||||||
{
|
|
||||||
this.FadeOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
@ -137,6 +126,13 @@ namespace osu.Game.Screens.Play
|
|||||||
remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, Easing.OutQuint);
|
remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseMove(InputState state)
|
||||||
|
{
|
||||||
|
if (!state.Mouse.HasAnyButtonPressed)
|
||||||
|
fadeContainer.State = Visibility.Visible;
|
||||||
|
return base.OnMouseMove(state);
|
||||||
|
}
|
||||||
|
|
||||||
public bool OnPressed(GlobalAction action)
|
public bool OnPressed(GlobalAction action)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action)
|
||||||
@ -176,7 +172,7 @@ namespace osu.Game.Screens.Play
|
|||||||
if (stateChanged)
|
if (stateChanged)
|
||||||
this.FadeIn(500, Easing.OutExpo);
|
this.FadeIn(500, Easing.OutExpo);
|
||||||
|
|
||||||
if (!IsHovered)
|
if (!IsHovered && !IsDragged)
|
||||||
using (BeginDelayedSequence(1000))
|
using (BeginDelayedSequence(1000))
|
||||||
scheduledHide = Schedule(() => State = Visibility.Hidden);
|
scheduledHide = Schedule(() => State = Visibility.Hidden);
|
||||||
break;
|
break;
|
||||||
@ -194,6 +190,18 @@ namespace osu.Game.Screens.Play
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
State = Visibility.Visible;
|
State = Visibility.Visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||||
|
{
|
||||||
|
scheduledHide?.Cancel();
|
||||||
|
return base.OnMouseDown(state, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||||
|
{
|
||||||
|
State = Visibility.Visible;
|
||||||
|
return base.OnMouseUp(state, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Button : OsuClickableContainer
|
private class Button : OsuClickableContainer
|
||||||
@ -274,7 +282,7 @@ namespace osu.Game.Screens.Play
|
|||||||
flow.TransformSpacingTo(new Vector2(5), 500, Easing.OutQuint);
|
flow.TransformSpacingTo(new Vector2(5), 500, Easing.OutQuint);
|
||||||
box.FadeColour(colourHover, 500, Easing.OutQuint);
|
box.FadeColour(colourHover, 500, Easing.OutQuint);
|
||||||
background.FadeTo(0.4f, 500, Easing.OutQuint);
|
background.FadeTo(0.4f, 500, Easing.OutQuint);
|
||||||
return base.OnHover(state);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnHoverLost(InputState state)
|
protected override void OnHoverLost(InputState state)
|
@ -364,10 +364,8 @@
|
|||||||
<Compile Include="Overlays\Volume\VolumeMeter.cs" />
|
<Compile Include="Overlays\Volume\VolumeMeter.cs" />
|
||||||
<Compile Include="Rulesets\Configuration\IRulesetConfigManager.cs" />
|
<Compile Include="Rulesets\Configuration\IRulesetConfigManager.cs" />
|
||||||
<Compile Include="Rulesets\Configuration\RulesetConfigManager.cs" />
|
<Compile Include="Rulesets\Configuration\RulesetConfigManager.cs" />
|
||||||
<Compile Include="Rulesets\Edit\Layers\BorderLayer.cs" />
|
<Compile Include="Rulesets\Edit\HitObjectMask.cs" />
|
||||||
<Compile Include="Rulesets\Edit\Layers\Selection\CaptureBox.cs" />
|
<Compile Include="Rulesets\Edit\Types\IHasEditablePosition.cs" />
|
||||||
<Compile Include="Rulesets\Edit\Layers\Selection\HitObjectOverlay.cs" />
|
|
||||||
<Compile Include="Rulesets\Edit\Layers\Selection\HitObjectOverlayLayer.cs" />
|
|
||||||
<Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" />
|
<Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" />
|
||||||
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
|
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
|
||||||
<Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" />
|
<Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" />
|
||||||
@ -382,6 +380,10 @@
|
|||||||
<Compile Include="Rulesets\Replays\Types\IConvertibleReplayFrame.cs" />
|
<Compile Include="Rulesets\Replays\Types\IConvertibleReplayFrame.cs" />
|
||||||
<Compile Include="Rulesets\Scoring\Legacy\LegacyScoreParser.cs" />
|
<Compile Include="Rulesets\Scoring\Legacy\LegacyScoreParser.cs" />
|
||||||
<Compile Include="Rulesets\UI\JudgementContainer.cs" />
|
<Compile Include="Rulesets\UI\JudgementContainer.cs" />
|
||||||
|
<Compile Include="Screens\Edit\Screens\Compose\Layers\BorderLayer.cs" />
|
||||||
|
<Compile Include="Screens\Edit\Screens\Compose\Layers\HitObjectMaskLayer.cs" />
|
||||||
|
<Compile Include="Screens\Edit\Screens\Compose\Layers\SelectionBox.cs" />
|
||||||
|
<Compile Include="Screens\Edit\Screens\Compose\Layers\SelectionLayer.cs" />
|
||||||
<Compile Include="Screens\Play\ScreenWithBeatmapBackground.cs" />
|
<Compile Include="Screens\Play\ScreenWithBeatmapBackground.cs" />
|
||||||
<Compile Include="Screens\Play\PlayerSettings\VisualSettings.cs" />
|
<Compile Include="Screens\Play\PlayerSettings\VisualSettings.cs" />
|
||||||
<Compile Include="Rulesets\Objects\CatmullApproximator.cs" />
|
<Compile Include="Rulesets\Objects\CatmullApproximator.cs" />
|
||||||
@ -396,8 +398,6 @@
|
|||||||
<Compile Include="Rulesets\UI\Scrolling\ScrollingRulesetContainer.cs" />
|
<Compile Include="Rulesets\UI\Scrolling\ScrollingRulesetContainer.cs" />
|
||||||
<Compile Include="Screens\Select\ImportFromStablePopup.cs" />
|
<Compile Include="Screens\Select\ImportFromStablePopup.cs" />
|
||||||
<Compile Include="Overlays\Settings\SettingsButton.cs" />
|
<Compile Include="Overlays\Settings\SettingsButton.cs" />
|
||||||
<Compile Include="Rulesets\Edit\Layers\Selection\SelectionBox.cs" />
|
|
||||||
<Compile Include="Rulesets\Edit\Layers\Selection\SelectionLayer.cs" />
|
|
||||||
<Compile Include="Screens\Edit\Components\BottomBarContainer.cs" />
|
<Compile Include="Screens\Edit\Components\BottomBarContainer.cs" />
|
||||||
<Compile Include="Screens\Edit\Components\PlaybackControl.cs" />
|
<Compile Include="Screens\Edit\Components\PlaybackControl.cs" />
|
||||||
<Compile Include="Screens\Edit\Components\TimeInfoContainer.cs" />
|
<Compile Include="Screens\Edit\Components\TimeInfoContainer.cs" />
|
||||||
@ -811,7 +811,7 @@
|
|||||||
<Compile Include="Screens\Play\PlayerSettings\PlayerCheckbox.cs" />
|
<Compile Include="Screens\Play\PlayerSettings\PlayerCheckbox.cs" />
|
||||||
<Compile Include="Screens\Play\PlayerSettings\PlayerSettingsGroup.cs" />
|
<Compile Include="Screens\Play\PlayerSettings\PlayerSettingsGroup.cs" />
|
||||||
<Compile Include="Screens\Play\PlayerSettings\PlayerSliderBar.cs" />
|
<Compile Include="Screens\Play\PlayerSettings\PlayerSliderBar.cs" />
|
||||||
<Compile Include="Screens\Play\SkipButton.cs" />
|
<Compile Include="Screens\Play\SkipOverlay.cs" />
|
||||||
<Compile Include="Screens\Play\SongProgress.cs" />
|
<Compile Include="Screens\Play\SongProgress.cs" />
|
||||||
<Compile Include="Screens\Play\SongProgressBar.cs" />
|
<Compile Include="Screens\Play\SongProgressBar.cs" />
|
||||||
<Compile Include="Screens\Play\SongProgressGraph.cs" />
|
<Compile Include="Screens\Play\SongProgressGraph.cs" />
|
||||||
|
Loading…
Reference in New Issue
Block a user