mirror of
https://github.com/ppy/osu.git
synced 2024-11-14 16:37:26 +08:00
Simplify timing point display on timeline
This commit is contained in:
parent
a0002943a1
commit
3065f808a7
@ -78,7 +78,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
private TimelineTickDisplay ticks = null!;
|
private TimelineTickDisplay ticks = null!;
|
||||||
|
|
||||||
private TimelineControlPointDisplay controlPoints = null!;
|
private TimelineTimingChangeDisplay controlPoints = null!;
|
||||||
|
|
||||||
private Container mainContent = null!;
|
private Container mainContent = null!;
|
||||||
|
|
||||||
@ -117,10 +117,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
AddRange(new Drawable[]
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
controlPoints = new TimelineControlPointDisplay
|
ticks = new TimelineTickDisplay(),
|
||||||
|
controlPoints = new TimelineTimingChangeDisplay
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = timeline_expanded_height,
|
Height = timeline_expanded_height - timeline_height,
|
||||||
},
|
},
|
||||||
ticks,
|
ticks,
|
||||||
mainContent = new Container
|
mainContent = new Container
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Caching;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The part of the timeline that displays the control points.
|
|
||||||
/// </summary>
|
|
||||||
public partial class TimelineControlPointDisplay : TimelinePart<TimelineControlPointGroup>
|
|
||||||
{
|
|
||||||
[Resolved]
|
|
||||||
private Timeline timeline { get; set; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The visible time/position range of the timeline.
|
|
||||||
/// </summary>
|
|
||||||
private (float min, float max) visibleRange = (float.MinValue, float.MaxValue);
|
|
||||||
|
|
||||||
private readonly Cached groupCache = new Cached();
|
|
||||||
|
|
||||||
private readonly IBindableList<ControlPointGroup> controlPointGroups = new BindableList<ControlPointGroup>();
|
|
||||||
|
|
||||||
protected override void LoadBeatmap(EditorBeatmap beatmap)
|
|
||||||
{
|
|
||||||
base.LoadBeatmap(beatmap);
|
|
||||||
|
|
||||||
controlPointGroups.UnbindAll();
|
|
||||||
controlPointGroups.BindTo(beatmap.ControlPointInfo.Groups);
|
|
||||||
controlPointGroups.BindCollectionChanged((_, _) => groupCache.Invalidate(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
if (DrawWidth <= 0) return;
|
|
||||||
|
|
||||||
(float, float) newRange = (
|
|
||||||
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X - TopPointPiece.WIDTH) / DrawWidth * Content.RelativeChildSize.X,
|
|
||||||
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X) / DrawWidth * Content.RelativeChildSize.X);
|
|
||||||
|
|
||||||
if (visibleRange != newRange)
|
|
||||||
{
|
|
||||||
visibleRange = newRange;
|
|
||||||
groupCache.Invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!groupCache.IsValid)
|
|
||||||
{
|
|
||||||
recreateDrawableGroups();
|
|
||||||
groupCache.Validate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void recreateDrawableGroups()
|
|
||||||
{
|
|
||||||
// Remove groups outside the visible range
|
|
||||||
foreach (TimelineControlPointGroup drawableGroup in this)
|
|
||||||
{
|
|
||||||
if (!shouldBeVisible(drawableGroup.Group))
|
|
||||||
drawableGroup.Expire();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add remaining ones
|
|
||||||
for (int i = 0; i < controlPointGroups.Count; i++)
|
|
||||||
{
|
|
||||||
var group = controlPointGroups[i];
|
|
||||||
|
|
||||||
if (!shouldBeVisible(group))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool alreadyVisible = false;
|
|
||||||
|
|
||||||
foreach (var g in this)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(g.Group, group))
|
|
||||||
{
|
|
||||||
alreadyVisible = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alreadyVisible)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Add(new TimelineControlPointGroup(group));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool shouldBeVisible(ControlPointGroup group) => group.Time >= visibleRange.min && group.Time <= visibleRange.max;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|
||||||
{
|
|
||||||
public partial class TimelineControlPointGroup : CompositeDrawable
|
|
||||||
{
|
|
||||||
public readonly ControlPointGroup Group;
|
|
||||||
|
|
||||||
private readonly IBindableList<ControlPoint> controlPoints = new BindableList<ControlPoint>();
|
|
||||||
|
|
||||||
public TimelineControlPointGroup(ControlPointGroup group)
|
|
||||||
{
|
|
||||||
Group = group;
|
|
||||||
|
|
||||||
RelativePositionAxes = Axes.X;
|
|
||||||
RelativeSizeAxes = Axes.Y;
|
|
||||||
AutoSizeAxes = Axes.X;
|
|
||||||
|
|
||||||
Origin = Anchor.TopLeft;
|
|
||||||
|
|
||||||
// offset visually to avoid overlapping timeline tick display.
|
|
||||||
X = (float)group.Time + 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
controlPoints.BindTo(Group.ControlPoints);
|
|
||||||
controlPoints.BindCollectionChanged((_, _) =>
|
|
||||||
{
|
|
||||||
ClearInternal();
|
|
||||||
|
|
||||||
foreach (var point in controlPoints)
|
|
||||||
{
|
|
||||||
switch (point)
|
|
||||||
{
|
|
||||||
case TimingControlPoint timingPoint:
|
|
||||||
AddInternal(new TimingPointPiece(timingPoint));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,164 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Caching;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
||||||
|
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The part of the timeline that displays the control points.
|
||||||
|
/// </summary>
|
||||||
|
public partial class TimelineTimingChangeDisplay : TimelinePart<TimelineTimingChangeDisplay.TimingPointPiece>
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private Timeline timeline { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The visible time/position range of the timeline.
|
||||||
|
/// </summary>
|
||||||
|
private (float min, float max) visibleRange = (float.MinValue, float.MaxValue);
|
||||||
|
|
||||||
|
private readonly Cached groupCache = new Cached();
|
||||||
|
|
||||||
|
private ControlPointInfo controlPointInfo = null!;
|
||||||
|
|
||||||
|
protected override void LoadBeatmap(EditorBeatmap beatmap)
|
||||||
|
{
|
||||||
|
base.LoadBeatmap(beatmap);
|
||||||
|
|
||||||
|
beatmap.ControlPointInfo.ControlPointsChanged += () => groupCache.Invalidate();
|
||||||
|
controlPointInfo = beatmap.ControlPointInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (DrawWidth <= 0) return;
|
||||||
|
|
||||||
|
(float, float) newRange = (
|
||||||
|
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X - TimingPointPiece.WIDTH) / DrawWidth * Content.RelativeChildSize.X,
|
||||||
|
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X + TimingPointPiece.WIDTH) / DrawWidth * Content.RelativeChildSize.X);
|
||||||
|
|
||||||
|
if (visibleRange != newRange)
|
||||||
|
{
|
||||||
|
visibleRange = newRange;
|
||||||
|
groupCache.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!groupCache.IsValid)
|
||||||
|
{
|
||||||
|
recreateDrawableGroups();
|
||||||
|
groupCache.Validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recreateDrawableGroups()
|
||||||
|
{
|
||||||
|
// Remove groups outside the visible range (or timing points which have since been removed from the beatmap).
|
||||||
|
foreach (TimingPointPiece drawableGroup in this)
|
||||||
|
{
|
||||||
|
if (!controlPointInfo.TimingPoints.Contains(drawableGroup.Point) || !shouldBeVisible(drawableGroup.Point))
|
||||||
|
drawableGroup.Expire();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add remaining / new ones.
|
||||||
|
foreach (TimingControlPoint t in controlPointInfo.TimingPoints)
|
||||||
|
attemptAddTimingPoint(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void attemptAddTimingPoint(TimingControlPoint point)
|
||||||
|
{
|
||||||
|
if (!shouldBeVisible(point))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var child in this)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(child.Point, point))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Add(new TimingPointPiece(point));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool shouldBeVisible(TimingControlPoint point) => point.Time >= visibleRange.min && point.Time <= visibleRange.max;
|
||||||
|
|
||||||
|
public partial class TimingPointPiece : CompositeDrawable
|
||||||
|
{
|
||||||
|
public const float WIDTH = 16;
|
||||||
|
|
||||||
|
public readonly TimingControlPoint Point;
|
||||||
|
|
||||||
|
private readonly BindableNumber<double> beatLength;
|
||||||
|
|
||||||
|
protected OsuSpriteText Label { get; private set; } = null!;
|
||||||
|
|
||||||
|
public TimingPointPiece(TimingControlPoint timingPoint)
|
||||||
|
{
|
||||||
|
RelativePositionAxes = Axes.X;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
Width = WIDTH;
|
||||||
|
|
||||||
|
Origin = Anchor.TopRight;
|
||||||
|
|
||||||
|
Point = timingPoint;
|
||||||
|
|
||||||
|
beatLength = timingPoint.BeatLengthBindable.GetBoundCopy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
const float corner_radius = PointVisualisation.MAX_WIDTH / 2;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Point.GetRepresentingColour(colours),
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = corner_radius,
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
Colour = Color4.White,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Label = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Rotation = 90,
|
||||||
|
Padding = new MarginPadding { Horizontal = 2 },
|
||||||
|
Font = OsuFont.Default.With(size: 12, weight: FontWeight.SemiBold),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
beatLength.BindValueChanged(beatLength =>
|
||||||
|
{
|
||||||
|
Label.Text = $"{60000 / beatLength.NewValue:n1} BPM";
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
X = (float)Point.Time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|
||||||
{
|
|
||||||
public partial class TimingPointPiece : TopPointPiece
|
|
||||||
{
|
|
||||||
private readonly BindableNumber<double> beatLength;
|
|
||||||
|
|
||||||
public TimingPointPiece(TimingControlPoint point)
|
|
||||||
: base(point)
|
|
||||||
{
|
|
||||||
beatLength = point.BeatLengthBindable.GetBoundCopy();
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
beatLength.BindValueChanged(beatLength =>
|
|
||||||
{
|
|
||||||
Label.Text = $"{60000 / beatLength.NewValue:n1} BPM";
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|
||||||
{
|
|
||||||
public partial class TopPointPiece : CompositeDrawable
|
|
||||||
{
|
|
||||||
protected readonly ControlPoint Point;
|
|
||||||
|
|
||||||
protected OsuSpriteText Label { get; private set; } = null!;
|
|
||||||
|
|
||||||
public const float WIDTH = 80;
|
|
||||||
|
|
||||||
public TopPointPiece(ControlPoint point)
|
|
||||||
{
|
|
||||||
Point = point;
|
|
||||||
Width = WIDTH;
|
|
||||||
Height = 16;
|
|
||||||
Margin = new MarginPadding { Vertical = 4 };
|
|
||||||
|
|
||||||
Origin = Anchor.TopCentre;
|
|
||||||
Anchor = Anchor.TopCentre;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
const float corner_radius = 4;
|
|
||||||
const float arrow_extension = 3;
|
|
||||||
const float triangle_portion = 15;
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
|
||||||
// This is a triangle, trust me.
|
|
||||||
// Doing it this way looks okay. Doing it using Triangle primitive is basically impossible.
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Colour = Point.GetRepresentingColour(colours),
|
|
||||||
X = -corner_radius,
|
|
||||||
Size = new Vector2(triangle_portion * arrow_extension, Height),
|
|
||||||
Anchor = Anchor.CentreRight,
|
|
||||||
Origin = Anchor.CentreRight,
|
|
||||||
Masking = true,
|
|
||||||
CornerRadius = Height,
|
|
||||||
CornerExponent = 1.4f,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Colour = Color4.White,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Width = WIDTH - triangle_portion,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Colour = Point.GetRepresentingColour(colours),
|
|
||||||
Masking = true,
|
|
||||||
CornerRadius = corner_radius,
|
|
||||||
Child = new Box
|
|
||||||
{
|
|
||||||
Colour = Color4.White,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Label = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Padding = new MarginPadding(3),
|
|
||||||
Font = OsuFont.Default.With(size: 14, weight: FontWeight.SemiBold),
|
|
||||||
Colour = colours.B5,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user