1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-15 00:02:54 +08:00

Move SummaryTimeline into /Edit/Components/SummaryTimeline

This commit is contained in:
smoogipooo 2017-09-20 16:59:03 +09:00
parent 3da3ef1a50
commit f9568619e7
3 changed files with 361 additions and 336 deletions

View File

@ -0,0 +1,356 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
using osu.Game.Graphics;
namespace osu.Game.Screens.Edit.Components
{
/// <summary>
/// The timeline that sits at the bottom of the editor.
/// </summary>
public class SummaryTimeline : CompositeDrawable
{
private const float corner_radius = 5;
private const float contents_padding = 15;
private const float marker_bar_width = 2;
private readonly Drawable background;
private readonly Container markerContainer;
private readonly Drawable timelineBar;
private readonly Drawable marker;
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
public SummaryTimeline()
{
Masking = true;
CornerRadius = 5;
InternalChildren = new[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = contents_padding, Right = contents_padding },
Children = new[]
{
markerContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Child = marker = new Container
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
RelativePositionAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Children = new Drawable[]
{
new Triangle
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
Scale = new Vector2(1, -1),
Size = new Vector2(10, 5),
},
new Triangle
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Size = new Vector2(10, 5)
},
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Width = 2,
EdgeSmoothness = new Vector2(1, 0)
}
}
}
},
new ControlPointTimeline
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
new BookmarkTimeline
{
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
timelineBar = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Circle
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Size = new Vector2(5)
},
new Box
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
EdgeSmoothness = new Vector2(0, 1),
},
new Circle
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreLeft,
Size = new Vector2(5)
},
}
},
new BreakTimeline
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0.25f
}
}
}
};
}
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame, OsuColour colours)
{
background.Colour = colours.Gray1;
marker.Colour = colours.Red;
timelineBar.Colour = colours.Gray5;
beatmap.BindTo(osuGame.Beatmap);
markerContainer.RelativeChildSize = new Vector2((float)Math.Max(1, beatmap.Value.Track.Length), 1);
beatmap.ValueChanged += b => markerContainer.RelativeChildSize = new Vector2((float)Math.Max(1, b.Track.Length), 1);
}
protected override bool OnDragStart(InputState state) => true;
protected override bool OnDragEnd(InputState state) => true;
protected override bool OnDrag(InputState state)
{
seekToPosition(state.Mouse.NativeState.Position);
return true;
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
seekToPosition(state.Mouse.NativeState.Position);
return true;
}
/// <summary>
/// Seeks the <see cref="SummaryTimeline"/> to the time closest to a position on the screen relative to the <see cref="SummaryTimeline"/>.
/// </summary>
/// <param name="screenPosition">The position in screen coordinates.</param>
private void seekToPosition(Vector2 screenPosition)
{
float markerPos = MathHelper.Clamp(markerContainer.ToLocalSpace(screenPosition).X, 0, markerContainer.DrawWidth);
seekTo(markerPos / markerContainer.DrawWidth * beatmap.Value.Track.Length);
}
private void seekTo(double time) => beatmap.Value.Track.Seek(time);
protected override void Update()
{
base.Update();
marker.X = (float)beatmap.Value.Track.CurrentTime;
}
/// <summary>
/// The part of the timeline that displays the control points.
/// </summary>
private class ControlPointTimeline : Timeline
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{
ControlPointInfo cpi = beatmap.Beatmap.ControlPointInfo;
cpi.TimingPoints.ForEach(addTimingPoint);
// Consider all non-timing points as the same type
cpi.SoundPoints.Select(c => (ControlPoint)c)
.Concat(cpi.EffectPoints)
.Concat(cpi.DifficultyPoints)
.Distinct()
// Non-timing points should not be added where there are timing points
.Where(c => cpi.TimingPointAt(c.Time).Time != c.Time)
.ForEach(addNonTimingPoint);
}
private void addTimingPoint(ControlPoint controlPoint) => Add(new TimingPointVisualisation(controlPoint));
private void addNonTimingPoint(ControlPoint controlPoint) => Add(new NonTimingPointVisualisation(controlPoint));
private class TimingPointVisualisation : ControlPointVisualisation
{
public TimingPointVisualisation(ControlPoint controlPoint)
: base(controlPoint)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.YellowDark;
}
private class NonTimingPointVisualisation : ControlPointVisualisation
{
public NonTimingPointVisualisation(ControlPoint controlPoint)
: base(controlPoint)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Green;
}
private abstract class ControlPointVisualisation : PointVisualisation
{
protected ControlPointVisualisation(ControlPoint controlPoint)
: base(controlPoint.Time)
{
}
}
}
/// <summary>
/// The part of the timeline that displays bookmarks.
/// </summary>
private class BookmarkTimeline : Timeline
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{
foreach (int bookmark in beatmap.BeatmapInfo.Bookmarks)
Add(new BookmarkVisualisation(bookmark));
}
private class BookmarkVisualisation : PointVisualisation
{
public BookmarkVisualisation(double startTime)
: base(startTime)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Blue;
}
}
/// <summary>
/// The part of the timeline that displays breaks in the song.
/// </summary>
private class BreakTimeline : Timeline
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{
foreach (var breakPeriod in beatmap.Beatmap.Breaks)
Add(new BreakVisualisation(breakPeriod));
}
private class BreakVisualisation : DurationVisualisation
{
public BreakVisualisation(BreakPeriod breakPeriod)
: base(breakPeriod.StartTime, breakPeriod.EndTime)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Yellow;
}
}
/// <summary>
/// Represents a part of the editor timeline.
/// </summary>
private abstract class Timeline : CompositeDrawable
{
private readonly Container timeline;
protected Timeline()
{
AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both });
}
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame)
{
osuGame.Beatmap.ValueChanged += b =>
{
timeline.Clear();
timeline.RelativeChildSize = new Vector2((float)Math.Max(1, b.Track.Length), 1);
LoadBeatmap(b);
};
timeline.RelativeChildSize = new Vector2((float)Math.Max(1, osuGame.Beatmap.Value.Track.Length), 1);
LoadBeatmap(osuGame.Beatmap);
}
protected void Add(PointVisualisation visualisation) => timeline.Add(visualisation);
protected void Add(DurationVisualisation visualisation) => timeline.Add(visualisation);
protected abstract void LoadBeatmap(WorkingBeatmap beatmap);
}
/// <summary>
/// Represents a singular point on a <see cref="Timeline"/>.
/// </summary>
private class PointVisualisation : Box
{
protected PointVisualisation(double startTime)
{
Origin = Anchor.TopCentre;
RelativeSizeAxes = Axes.Y;
Width = 1;
EdgeSmoothness = new Vector2(1, 0);
RelativePositionAxes = Axes.X;
X = (float)startTime;
}
}
/// <summary>
/// Represents a spanning point on a <see cref="Timeline"/>.
/// </summary>
private class DurationVisualisation : Container
{
protected DurationVisualisation(double startTime, double endTime)
{
Masking = true;
CornerRadius = corner_radius;
RelativePositionAxes = Axes.X;
RelativeSizeAxes = Axes.Both;
X = (float)startTime;
Width = (float)(endTime - startTime);
AddInternal(new Box { RelativeSizeAxes = Axes.Both });
}
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using osu.Framework.Allocation;
@ -17,6 +18,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components;
namespace osu.Game.Tests.Visual
{
@ -25,6 +27,8 @@ namespace osu.Game.Tests.Visual
private const int length = 60000;
private readonly Random random;
public override IReadOnlyList<Type> RequiredTypes => new Type[] { typeof(SummaryTimeline) };
public TestCaseEditorSummaryTimeline()
{
random = new Random(1337);
@ -85,341 +89,5 @@ namespace osu.Game.Tests.Visual
}
}
}
/// <summary>
/// The timeline that sits at the bottom of the editor.
/// </summary>
private class SummaryTimeline : CompositeDrawable
{
private const float corner_radius = 5;
private const float contents_padding = 15;
private const float marker_bar_width = 2;
private readonly Drawable background;
private readonly Container markerContainer;
private readonly Drawable timelineBar;
private readonly Drawable marker;
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
public SummaryTimeline()
{
Masking = true;
CornerRadius = 5;
InternalChildren = new[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = contents_padding, Right = contents_padding },
Children = new[]
{
markerContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Child = marker = new Container
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
RelativePositionAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Children = new Drawable[]
{
new Triangle
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
Scale = new Vector2(1, -1),
Size = new Vector2(10, 5),
},
new Triangle
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Size = new Vector2(10, 5)
},
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Width = 2,
EdgeSmoothness = new Vector2(1, 0)
}
}
}
},
new ControlPointTimeline
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
new BookmarkTimeline
{
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
timelineBar = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Circle
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Size = new Vector2(5)
},
new Box
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
EdgeSmoothness = new Vector2(0, 1),
},
new Circle
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreLeft,
Size = new Vector2(5)
},
}
},
new BreakTimeline
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0.25f
}
}
}
};
}
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame, OsuColour colours)
{
background.Colour = colours.Gray1;
marker.Colour = colours.Red;
timelineBar.Colour = colours.Gray5;
beatmap.BindTo(osuGame.Beatmap);
markerContainer.RelativeChildSize = new Vector2((float)Math.Max(1, beatmap.Value.Track.Length), 1);
beatmap.ValueChanged += b => markerContainer.RelativeChildSize = new Vector2((float)Math.Max(1, b.Track.Length), 1);
}
protected override bool OnDragStart(InputState state) => true;
protected override bool OnDragEnd(InputState state) => true;
protected override bool OnDrag(InputState state)
{
seekToPosition(state.Mouse.NativeState.Position);
return true;
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
seekToPosition(state.Mouse.NativeState.Position);
return true;
}
/// <summary>
/// Seeks the <see cref="SummaryTimeline"/> to the time closest to a position on the screen relative to the <see cref="SummaryTimeline"/>.
/// </summary>
/// <param name="screenPosition">The position in screen coordinates.</param>
private void seekToPosition(Vector2 screenPosition)
{
float markerPos = MathHelper.Clamp(markerContainer.ToLocalSpace(screenPosition).X, 0, markerContainer.DrawWidth);
seekTo(markerPos / markerContainer.DrawWidth * beatmap.Value.Track.Length);
}
private void seekTo(double time) => beatmap.Value.Track.Seek(time);
protected override void Update()
{
base.Update();
marker.X = (float)beatmap.Value.Track.CurrentTime;
}
/// <summary>
/// The part of the timeline that displays the control points.
/// </summary>
private class ControlPointTimeline : Timeline
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{
ControlPointInfo cpi = beatmap.Beatmap.ControlPointInfo;
cpi.TimingPoints.ForEach(addTimingPoint);
// Consider all non-timing points as the same type
cpi.SoundPoints.Select(c => (ControlPoint)c)
.Concat(cpi.EffectPoints)
.Concat(cpi.DifficultyPoints)
.Distinct()
// Non-timing points should not be added where there are timing points
.Where(c => cpi.TimingPointAt(c.Time).Time != c.Time)
.ForEach(addNonTimingPoint);
}
private void addTimingPoint(ControlPoint controlPoint) => Add(new TimingPointVisualisation(controlPoint));
private void addNonTimingPoint(ControlPoint controlPoint) => Add(new NonTimingPointVisualisation(controlPoint));
private class TimingPointVisualisation : ControlPointVisualisation
{
public TimingPointVisualisation(ControlPoint controlPoint)
: base(controlPoint)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.YellowDark;
}
private class NonTimingPointVisualisation : ControlPointVisualisation
{
public NonTimingPointVisualisation(ControlPoint controlPoint)
: base(controlPoint)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Green;
}
private abstract class ControlPointVisualisation : PointVisualisation
{
protected ControlPointVisualisation(ControlPoint controlPoint)
: base(controlPoint.Time)
{
}
}
}
/// <summary>
/// The part of the timeline that displays bookmarks.
/// </summary>
private class BookmarkTimeline : Timeline
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{
foreach (int bookmark in beatmap.BeatmapInfo.Bookmarks)
Add(new BookmarkVisualisation(bookmark));
}
private class BookmarkVisualisation : PointVisualisation
{
public BookmarkVisualisation(double startTime)
: base(startTime)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Blue;
}
}
/// <summary>
/// The part of the timeline that displays breaks in the song.
/// </summary>
private class BreakTimeline : Timeline
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{
foreach (var breakPeriod in beatmap.Beatmap.Breaks)
Add(new BreakVisualisation(breakPeriod));
}
private class BreakVisualisation : DurationVisualisation
{
public BreakVisualisation(BreakPeriod breakPeriod)
: base(breakPeriod.StartTime, breakPeriod.EndTime)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Yellow;
}
}
/// <summary>
/// Represents a part of the editor timeline.
/// </summary>
private abstract class Timeline : CompositeDrawable
{
private readonly Container timeline;
protected Timeline()
{
AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both });
}
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame)
{
osuGame.Beatmap.ValueChanged += b =>
{
timeline.Clear();
timeline.RelativeChildSize = new Vector2((float)Math.Max(1, b.Track.Length), 1);
LoadBeatmap(b);
};
timeline.RelativeChildSize = new Vector2((float)Math.Max(1, osuGame.Beatmap.Value.Track.Length), 1);
LoadBeatmap(osuGame.Beatmap);
}
protected void Add(PointVisualisation visualisation) => timeline.Add(visualisation);
protected void Add(DurationVisualisation visualisation) => timeline.Add(visualisation);
protected abstract void LoadBeatmap(WorkingBeatmap beatmap);
}
/// <summary>
/// Represents a singular point on a <see cref="Timeline"/>.
/// </summary>
private class PointVisualisation : Box
{
protected PointVisualisation(double startTime)
{
Origin = Anchor.TopCentre;
RelativeSizeAxes = Axes.Y;
Width = 1;
EdgeSmoothness = new Vector2(1, 0);
RelativePositionAxes = Axes.X;
X = (float)startTime;
}
}
/// <summary>
/// Represents a spanning point on a <see cref="Timeline"/>.
/// </summary>
private class DurationVisualisation : Container
{
protected DurationVisualisation(double startTime, double endTime)
{
Masking = true;
CornerRadius = corner_radius;
RelativePositionAxes = Axes.X;
RelativeSizeAxes = Axes.Both;
X = (float)startTime;
Width = (float)(endTime - startTime);
AddInternal(new Box { RelativeSizeAxes = Axes.Both });
}
}
}
}
}

View File

@ -604,6 +604,7 @@
<Compile Include="Screens\Charts\ChartListing.cs" />
<Compile Include="Screens\Direct\OnlineListing.cs" />
<Compile Include="Screens\Edit\Editor.cs" />
<Compile Include="Screens\Edit\Components\SummaryTimeline.cs" />
<Compile Include="Screens\Edit\Menus\EditorMenuBar.cs" />
<Compile Include="Screens\Edit\Menus\EditorMenuBarItem.cs" />
<Compile Include="Screens\Edit\Menus\EditorMenuItem.cs" />