Merge branch 'master' into patch-1

This commit is contained in:
Dean Herbert 2017-09-26 19:28:03 +08:00 committed by GitHub
commit 00067d0d18
13 changed files with 622 additions and 14 deletions

@ -1 +1 @@
Subproject commit 5f3a7fe4d0537820a33b817a41623b4b22a3ec59
Subproject commit cdb031c3a8ef693cd71458c5e19c68127ab72938

View File

@ -78,15 +78,29 @@ namespace osu.Game.Beatmaps
// Editor
// This bookmarks stuff is necessary because DB doesn't know how to store int[]
public string StoredBookmarks { get; set; }
[JsonIgnore]
public string StoredBookmarks
{
get { return string.Join(",", Bookmarks); }
set
{
if (string.IsNullOrEmpty(value))
{
Bookmarks = new int[0];
return;
}
Bookmarks = value.Split(',').Select(v =>
{
int val;
bool result = int.TryParse(v, out val);
return new { result, val };
}).Where(p => p.result).Select(p => p.val).ToArray();
}
}
[Ignore]
[JsonIgnore]
public int[] Bookmarks
{
get { return StoredBookmarks.Split(',').Select(int.Parse).ToArray(); }
set { StoredBookmarks = string.Join(",", value); }
}
public int[] Bookmarks { get; set; } = new int[0];
public double DistanceSpacing { get; set; }
public int BeatDivisor { get; set; }

View File

@ -0,0 +1,33 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
/// <summary>
/// The part of the timeline that displays bookmarks.
/// </summary>
internal class BookmarkPart : TimelinePart
{
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;
}
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Timing;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
/// <summary>
/// The part of the timeline that displays breaks in the song.
/// </summary>
internal class BreakPart : TimelinePart
{
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;
}
}
}

View File

@ -0,0 +1,68 @@
// 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.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
/// <summary>
/// The part of the timeline that displays the control points.
/// </summary>
internal class ControlPointPart : TimelinePart
{
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)
{
}
}
}
}

View File

@ -0,0 +1,107 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
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;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
/// <summary>
/// The part of the timeline that displays the current position of the song.
/// </summary>
internal class MarkerPart : TimelinePart
{
private readonly Drawable marker;
public MarkerPart()
{
Add(marker = new MarkerVisualisation());
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
marker.Colour = colours.Red;
}
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)
{
if (Beatmap.Value == null)
return;
float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth);
seekTo(markerPos / 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 ?? 0);
}
private class MarkerVisualisation : CompositeDrawable
{
public MarkerVisualisation()
{
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
RelativePositionAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
AutoSizeAxes = Axes.X;
InternalChildren = 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)
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Red;
}
}
}

View File

@ -0,0 +1,40 @@
// 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 OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
/// <summary>
/// Represents a part of the summary timeline..
/// </summary>
internal abstract class TimelinePart : CompositeDrawable
{
public Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
private readonly Container timeline;
protected TimelinePart()
{
AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both });
Beatmap.ValueChanged += b =>
{
timeline.Clear();
timeline.RelativeChildSize = new Vector2((float)Math.Max(1, b.Track.Length), 1);
LoadBeatmap(b);
};
}
protected void Add(Drawable visualisation) => timeline.Add(visualisation);
protected virtual void LoadBeatmap(WorkingBeatmap beatmap)
{
}
}
}

View File

@ -0,0 +1,112 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary
{
/// <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;
public Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
private readonly Drawable background;
private readonly Drawable timelineBar;
public SummaryTimeline()
{
Masking = true;
CornerRadius = corner_radius;
TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart;
InternalChildren = new[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = contents_padding, Right = contents_padding },
Children = new[]
{
markerPart = new MarkerPart { RelativeSizeAxes = Axes.Both },
controlPointPart = new ControlPointPart
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
bookmarkPart = new BookmarkPart
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
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)
},
}
},
breakPart = new BreakPart
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0.25f
}
}
}
};
markerPart.Beatmap.BindTo(Beatmap);
controlPointPart.Beatmap.BindTo(Beatmap);
bookmarkPart.Beatmap.BindTo(Beatmap);
breakPart.Beatmap.BindTo(Beatmap);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray1;
timelineBar.Colour = colours.Gray5;
}
}
}

View File

@ -0,0 +1,28 @@
// Copyright (c) 2007-2017 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.Shapes;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
{
/// <summary>
/// Represents a spanning point on a timeline part.
/// </summary>
internal class DurationVisualisation : Container
{
protected DurationVisualisation(double startTime, double endTime)
{
Masking = true;
CornerRadius = 5;
RelativePositionAxes = Axes.X;
RelativeSizeAxes = Axes.Both;
X = (float)startTime;
Width = (float)(endTime - startTime);
AddInternal(new Box { RelativeSizeAxes = Axes.Both });
}
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
{
/// <summary>
/// Represents a singular point on a timeline part.
/// </summary>
internal 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;
}
}
}

View File

@ -1,29 +1,29 @@
// 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.Collections.Generic;
using OpenTK.Graphics;
using osu.Framework.Screens;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Select;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Menus;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
using OpenTK;
using osu.Framework.Allocation;
namespace osu.Game.Screens.Edit
{
internal class Editor : ScreenWhiteBox
internal class Editor : OsuScreen
{
protected override IEnumerable<Type> PossibleChildren => new[] { typeof(EditSongSelect) };
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
internal override bool ShowOverlays => false;
private readonly Box bottomBackground;
public Editor()
{
Add(new Container
@ -189,6 +189,49 @@ namespace osu.Game.Screens.Edit
}
}
});
SummaryTimeline summaryTimeline;
Add(new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 60,
Children = new Drawable[]
{
bottomBackground = new Box { RelativeSizeAxes = Axes.Both },
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 5, Bottom = 5, Left = 10, Right = 10 },
Child = new FillFlowContainer
{
Name = "Bottom bar",
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new[]
{
summaryTimeline = new SummaryTimeline
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Width = 0.65f
}
}
}
}
}
});
summaryTimeline.Beatmap.BindTo(Beatmap);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
bottomBackground.Colour = colours.Gray2;
}
protected override void OnResuming(Screen last)

View File

@ -0,0 +1,93 @@
// 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.Collections.Generic;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using OpenTK;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Framework.Configuration;
namespace osu.Game.Tests.Visual
{
internal class TestCaseEditorSummaryTimeline : OsuTestCase
{
private const int length = 60000;
private readonly Random random;
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(SummaryTimeline) };
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
public TestCaseEditorSummaryTimeline()
{
random = new Random(1337);
SummaryTimeline summaryTimeline;
Add(summaryTimeline = new SummaryTimeline
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500, 50)
});
summaryTimeline.Beatmap.BindTo(beatmap);
AddStep("New beatmap", newBeatmap);
newBeatmap();
}
private void newBeatmap()
{
var b = new Beatmap();
for (int i = 0; i < random.Next(1, 10); i++)
b.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { Time = random.Next(0, length) });
for (int i = 0; i < random.Next(1, 5); i++)
b.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint { Time = random.Next(0, length) });
for (int i = 0; i < random.Next(1, 5); i++)
b.ControlPointInfo.EffectPoints.Add(new EffectControlPoint { Time = random.Next(0, length) });
for (int i = 0; i < random.Next(1, 5); i++)
b.ControlPointInfo.SoundPoints.Add(new SoundControlPoint { Time = random.Next(0, length) });
b.BeatmapInfo.Bookmarks = new int[random.Next(10, 30)];
for (int i = 0; i < b.BeatmapInfo.Bookmarks.Length; i++)
b.BeatmapInfo.Bookmarks[i] = random.Next(0, length);
beatmap.Value = new TestWorkingBeatmap(b);
}
private class TestWorkingBeatmap : WorkingBeatmap
{
private readonly Beatmap beatmap;
public TestWorkingBeatmap(Beatmap beatmap)
: base(beatmap.BeatmapInfo)
{
this.beatmap = beatmap;
}
protected override Texture GetBackground() => null;
protected override Beatmap GetBeatmap() => beatmap;
protected override Track GetTrack() => new TestTrack();
private class TestTrack : TrackVirtual
{
public TestTrack()
{
Length = length;
}
}
}
}
}

View File

@ -606,6 +606,14 @@
<Compile Include="Screens\Charts\ChartListing.cs" />
<Compile Include="Screens\Direct\OnlineListing.cs" />
<Compile Include="Screens\Edit\Editor.cs" />
<Compile Include="Screens\Edit\Components\Timelines\Summary\Parts\BreakPart.cs" />
<Compile Include="Screens\Edit\Components\Timelines\Summary\Parts\BookmarkPart.cs" />
<Compile Include="Screens\Edit\Components\Timelines\Summary\Parts\ControlPointPart.cs" />
<Compile Include="Screens\Edit\Components\Timelines\Summary\Parts\MarkerPart.cs" />
<Compile Include="Screens\Edit\Components\Timelines\Summary\Parts\TimelinePart.cs" />
<Compile Include="Screens\Edit\Components\Timelines\Summary\Visualisations\DurationVisualisation.cs" />
<Compile Include="Screens\Edit\Components\Timelines\Summary\Visualisations\PointVisualisation.cs" />
<Compile Include="Screens\Edit\Components\Timelines\Summary\SummaryTimeline.cs" />
<Compile Include="Screens\Edit\Menus\EditorMenuBar.cs" />
<Compile Include="Screens\Edit\Menus\EditorMenuBarItem.cs" />
<Compile Include="Screens\Edit\Menus\EditorMenuItem.cs" />
@ -735,6 +743,7 @@
<Compile Include="Tests\Visual\TestCaseDrawableRoom.cs" />
<Compile Include="Tests\Visual\TestCaseDrawings.cs" />
<Compile Include="Tests\Visual\TestCaseEditorMenuBar.cs" />
<Compile Include="Tests\Visual\TestCaseEditorSummaryTimeline.cs" />
<Compile Include="Tests\Visual\TestCaseGamefield.cs" />
<Compile Include="Tests\Visual\TestCaseGraph.cs" />
<Compile Include="Tests\Visual\TestCaseKeyConfiguration.cs" />