mirror of
https://github.com/osukey/osukey.git
synced 2025-08-06 16:13:57 +09:00
Merge pull request #18668 from smoogipoo/editor-controlpoint-undo-redo
This commit is contained in:
@ -374,7 +374,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
double currentTime = timingPoint.Time;
|
double currentTime = timingPoint.Time;
|
||||||
|
|
||||||
while (!definitelyBigger(currentTime, mapEndTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint)
|
while (!definitelyBigger(currentTime, mapEndTime) && ReferenceEquals(controlPointInfo.TimingPointAt(currentTime), timingPoint))
|
||||||
{
|
{
|
||||||
beats.Add(Math.Floor(currentTime));
|
beats.Add(Math.Floor(currentTime));
|
||||||
i++;
|
i++;
|
||||||
|
@ -117,8 +117,8 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
// After placement these must be non-default as defaults are read-only.
|
// After placement these must be non-default as defaults are read-only.
|
||||||
AddAssert("Placed object has non-default control points", () =>
|
AddAssert("Placed object has non-default control points", () =>
|
||||||
EditorBeatmap.HitObjects[0].SampleControlPoint != SampleControlPoint.DEFAULT &&
|
!ReferenceEquals(EditorBeatmap.HitObjects[0].SampleControlPoint, SampleControlPoint.DEFAULT) &&
|
||||||
EditorBeatmap.HitObjects[0].DifficultyControlPoint != DifficultyControlPoint.DEFAULT);
|
!ReferenceEquals(EditorBeatmap.HitObjects[0].DifficultyControlPoint, DifficultyControlPoint.DEFAULT));
|
||||||
|
|
||||||
ReloadEditorToSameBeatmap();
|
ReloadEditorToSameBeatmap();
|
||||||
|
|
||||||
@ -126,8 +126,8 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
// After placement these must be non-default as defaults are read-only.
|
// After placement these must be non-default as defaults are read-only.
|
||||||
AddAssert("Placed object still has non-default control points", () =>
|
AddAssert("Placed object still has non-default control points", () =>
|
||||||
EditorBeatmap.HitObjects[0].SampleControlPoint != SampleControlPoint.DEFAULT &&
|
!ReferenceEquals(EditorBeatmap.HitObjects[0].SampleControlPoint, SampleControlPoint.DEFAULT) &&
|
||||||
EditorBeatmap.HitObjects[0].DifficultyControlPoint != DifficultyControlPoint.DEFAULT);
|
!ReferenceEquals(EditorBeatmap.HitObjects[0].DifficultyControlPoint, DifficultyControlPoint.DEFAULT));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -269,7 +269,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
private TimingControlPoint getNextTimingPoint(TimingControlPoint current)
|
private TimingControlPoint getNextTimingPoint(TimingControlPoint current)
|
||||||
{
|
{
|
||||||
if (timingPoints[^1] == current)
|
if (ReferenceEquals(timingPoints[^1], current))
|
||||||
return current;
|
return current;
|
||||||
|
|
||||||
int index = timingPoints.IndexOf(current); // -1 means that this is a "default beat"
|
int index = timingPoints.IndexOf(current); // -1 means that this is a "default beat"
|
||||||
@ -281,7 +281,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
if (timingPoints.Count == 0) return 0;
|
if (timingPoints.Count == 0) return 0;
|
||||||
|
|
||||||
if (timingPoints[^1] == current)
|
if (ReferenceEquals(timingPoints[^1], current))
|
||||||
{
|
{
|
||||||
Debug.Assert(BeatSyncSource.Clock != null);
|
Debug.Assert(BeatSyncSource.Clock != null);
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -11,7 +9,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Beatmaps.ControlPoints
|
namespace osu.Game.Beatmaps.ControlPoints
|
||||||
{
|
{
|
||||||
public abstract class ControlPoint : IComparable<ControlPoint>, IDeepCloneable<ControlPoint>
|
public abstract class ControlPoint : IComparable<ControlPoint>, IDeepCloneable<ControlPoint>, IEquatable<ControlPoint>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time at which the control point takes effect.
|
/// The time at which the control point takes effect.
|
||||||
@ -30,7 +28,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="existing">An existing control point to compare with.</param>
|
/// <param name="existing">An existing control point to compare with.</param>
|
||||||
/// <returns>Whether this <see cref="ControlPoint"/> is redundant when placed alongside <paramref name="existing"/>.</returns>
|
/// <returns>Whether this <see cref="ControlPoint"/> is redundant when placed alongside <paramref name="existing"/>.</returns>
|
||||||
public abstract bool IsRedundant(ControlPoint existing);
|
public abstract bool IsRedundant(ControlPoint? existing);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create an unbound copy of this control point.
|
/// Create an unbound copy of this control point.
|
||||||
@ -48,5 +46,20 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
{
|
{
|
||||||
Time = other.Time;
|
Time = other.Time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed override bool Equals(object? obj)
|
||||||
|
=> obj is ControlPoint otherControlPoint
|
||||||
|
&& Equals(otherControlPoint);
|
||||||
|
|
||||||
|
public virtual bool Equals(ControlPoint? other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(other, null)) return false;
|
||||||
|
if (ReferenceEquals(other, this)) return true;
|
||||||
|
|
||||||
|
return Time == other.Time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once NonReadonlyMemberInGetHashCode
|
||||||
|
public override int GetHashCode() => Time.GetHashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.ControlPoints
|
namespace osu.Game.Beatmaps.ControlPoints
|
||||||
{
|
{
|
||||||
public class ControlPointGroup : IComparable<ControlPointGroup>
|
public class ControlPointGroup : IComparable<ControlPointGroup>, IEquatable<ControlPointGroup>
|
||||||
{
|
{
|
||||||
public event Action<ControlPoint> ItemAdded;
|
public event Action<ControlPoint>? ItemAdded;
|
||||||
public event Action<ControlPoint> ItemRemoved;
|
public event Action<ControlPoint>? ItemRemoved;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time at which the control point takes effect.
|
/// The time at which the control point takes effect.
|
||||||
@ -48,5 +46,23 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
controlPoints.Remove(point);
|
controlPoints.Remove(point);
|
||||||
ItemRemoved?.Invoke(point);
|
ItemRemoved?.Invoke(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed override bool Equals(object? obj)
|
||||||
|
=> obj is ControlPointGroup otherGroup
|
||||||
|
&& Equals(otherGroup);
|
||||||
|
|
||||||
|
public virtual bool Equals(ControlPointGroup? other)
|
||||||
|
=> other != null
|
||||||
|
&& Time == other.Time
|
||||||
|
&& ControlPoints.SequenceEqual(other.ControlPoints);
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
HashCode hashCode = new HashCode();
|
||||||
|
hashCode.Add(Time);
|
||||||
|
foreach (var point in controlPoints)
|
||||||
|
hashCode.Add(point);
|
||||||
|
return hashCode.ToHashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
using System;
|
||||||
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -12,7 +11,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Note that going forward, this control point type should always be assigned directly to HitObjects.
|
/// Note that going forward, this control point type should always be assigned directly to HitObjects.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class DifficultyControlPoint : ControlPoint
|
public class DifficultyControlPoint : ControlPoint, IEquatable<DifficultyControlPoint>
|
||||||
{
|
{
|
||||||
public static readonly DifficultyControlPoint DEFAULT = new DifficultyControlPoint
|
public static readonly DifficultyControlPoint DEFAULT = new DifficultyControlPoint
|
||||||
{
|
{
|
||||||
@ -41,7 +40,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
set => SliderVelocityBindable.Value = value;
|
set => SliderVelocityBindable.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsRedundant(ControlPoint existing)
|
public override bool IsRedundant(ControlPoint? existing)
|
||||||
=> existing is DifficultyControlPoint existingDifficulty
|
=> existing is DifficultyControlPoint existingDifficulty
|
||||||
&& SliderVelocity == existingDifficulty.SliderVelocity;
|
&& SliderVelocity == existingDifficulty.SliderVelocity;
|
||||||
|
|
||||||
@ -51,5 +50,15 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
|
|
||||||
base.CopyFrom(other);
|
base.CopyFrom(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(ControlPoint? other)
|
||||||
|
=> other is DifficultyControlPoint otherDifficultyControlPoint
|
||||||
|
&& Equals(otherDifficultyControlPoint);
|
||||||
|
|
||||||
|
public bool Equals(DifficultyControlPoint? other)
|
||||||
|
=> base.Equals(other)
|
||||||
|
&& SliderVelocity == other.SliderVelocity;
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), SliderVelocity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
using System;
|
||||||
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.ControlPoints
|
namespace osu.Game.Beatmaps.ControlPoints
|
||||||
{
|
{
|
||||||
public class EffectControlPoint : ControlPoint
|
public class EffectControlPoint : ControlPoint, IEquatable<EffectControlPoint>
|
||||||
{
|
{
|
||||||
public static readonly EffectControlPoint DEFAULT = new EffectControlPoint
|
public static readonly EffectControlPoint DEFAULT = new EffectControlPoint
|
||||||
{
|
{
|
||||||
@ -68,7 +67,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
set => KiaiModeBindable.Value = value;
|
set => KiaiModeBindable.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsRedundant(ControlPoint existing)
|
public override bool IsRedundant(ControlPoint? existing)
|
||||||
=> !OmitFirstBarLine
|
=> !OmitFirstBarLine
|
||||||
&& existing is EffectControlPoint existingEffect
|
&& existing is EffectControlPoint existingEffect
|
||||||
&& KiaiMode == existingEffect.KiaiMode
|
&& KiaiMode == existingEffect.KiaiMode
|
||||||
@ -83,5 +82,17 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
|
|
||||||
base.CopyFrom(other);
|
base.CopyFrom(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(ControlPoint? other)
|
||||||
|
=> other is EffectControlPoint otherEffectControlPoint
|
||||||
|
&& Equals(otherEffectControlPoint);
|
||||||
|
|
||||||
|
public bool Equals(EffectControlPoint? other)
|
||||||
|
=> base.Equals(other)
|
||||||
|
&& OmitFirstBarLine == other.OmitFirstBarLine
|
||||||
|
&& ScrollSpeed == other.ScrollSpeed
|
||||||
|
&& KiaiMode == other.KiaiMode;
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), OmitFirstBarLine, ScrollSpeed, KiaiMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
using System;
|
||||||
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -13,7 +12,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Note that going forward, this control point type should always be assigned directly to HitObjects.
|
/// Note that going forward, this control point type should always be assigned directly to HitObjects.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class SampleControlPoint : ControlPoint
|
public class SampleControlPoint : ControlPoint, IEquatable<SampleControlPoint>
|
||||||
{
|
{
|
||||||
public const string DEFAULT_BANK = "normal";
|
public const string DEFAULT_BANK = "normal";
|
||||||
|
|
||||||
@ -73,7 +72,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
public virtual HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo)
|
public virtual HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo)
|
||||||
=> hitSampleInfo.With(newBank: hitSampleInfo.Bank ?? SampleBank, newVolume: hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume);
|
=> hitSampleInfo.With(newBank: hitSampleInfo.Bank ?? SampleBank, newVolume: hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume);
|
||||||
|
|
||||||
public override bool IsRedundant(ControlPoint existing)
|
public override bool IsRedundant(ControlPoint? existing)
|
||||||
=> existing is SampleControlPoint existingSample
|
=> existing is SampleControlPoint existingSample
|
||||||
&& SampleBank == existingSample.SampleBank
|
&& SampleBank == existingSample.SampleBank
|
||||||
&& SampleVolume == existingSample.SampleVolume;
|
&& SampleVolume == existingSample.SampleVolume;
|
||||||
@ -85,5 +84,16 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
|
|
||||||
base.CopyFrom(other);
|
base.CopyFrom(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(ControlPoint? other)
|
||||||
|
=> other is SampleControlPoint otherSampleControlPoint
|
||||||
|
&& Equals(otherSampleControlPoint);
|
||||||
|
|
||||||
|
public bool Equals(SampleControlPoint? other)
|
||||||
|
=> base.Equals(other)
|
||||||
|
&& SampleBank == other.SampleBank
|
||||||
|
&& SampleVolume == other.SampleVolume;
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), SampleBank, SampleVolume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -8,7 +9,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Beatmaps.ControlPoints
|
namespace osu.Game.Beatmaps.ControlPoints
|
||||||
{
|
{
|
||||||
public class TimingControlPoint : ControlPoint
|
public class TimingControlPoint : ControlPoint, IEquatable<TimingControlPoint>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time signature at this control point.
|
/// The time signature at this control point.
|
||||||
@ -68,7 +69,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
public double BPM => 60000 / BeatLength;
|
public double BPM => 60000 / BeatLength;
|
||||||
|
|
||||||
// Timing points are never redundant as they can change the time signature.
|
// Timing points are never redundant as they can change the time signature.
|
||||||
public override bool IsRedundant(ControlPoint existing) => false;
|
public override bool IsRedundant(ControlPoint? existing) => false;
|
||||||
|
|
||||||
public override void CopyFrom(ControlPoint other)
|
public override void CopyFrom(ControlPoint other)
|
||||||
{
|
{
|
||||||
@ -77,5 +78,16 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
|
|
||||||
base.CopyFrom(other);
|
base.CopyFrom(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(ControlPoint? other)
|
||||||
|
=> other is TimingControlPoint otherTimingControlPoint
|
||||||
|
&& Equals(otherTimingControlPoint);
|
||||||
|
|
||||||
|
public bool Equals(TimingControlPoint? other)
|
||||||
|
=> base.Equals(other)
|
||||||
|
&& TimeSignature.Equals(other.TimeSignature)
|
||||||
|
&& BeatLength.Equals(other.BeatLength);
|
||||||
|
|
||||||
|
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), TimeSignature, BeatLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
@ -145,7 +143,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
protected string CleanFilename(string path) => path.Trim('"').ToStandardisedPath();
|
protected string CleanFilename(string path) => path.Trim('"').ToStandardisedPath();
|
||||||
|
|
||||||
protected enum Section
|
public enum Section
|
||||||
{
|
{
|
||||||
General,
|
General,
|
||||||
Editor,
|
Editor,
|
||||||
@ -162,7 +160,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Do not use unless you're a legacy ruleset and 100% sure.")]
|
[Obsolete("Do not use unless you're a legacy ruleset and 100% sure.")]
|
||||||
public class LegacyDifficultyControlPoint : DifficultyControlPoint
|
public class LegacyDifficultyControlPoint : DifficultyControlPoint, IEquatable<LegacyDifficultyControlPoint>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it.
|
/// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it.
|
||||||
@ -188,9 +186,20 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
BpmMultiplier = ((LegacyDifficultyControlPoint)other).BpmMultiplier;
|
BpmMultiplier = ((LegacyDifficultyControlPoint)other).BpmMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(ControlPoint? other)
|
||||||
|
=> other is LegacyDifficultyControlPoint otherLegacyDifficultyControlPoint
|
||||||
|
&& Equals(otherLegacyDifficultyControlPoint);
|
||||||
|
|
||||||
|
public bool Equals(LegacyDifficultyControlPoint? other)
|
||||||
|
=> base.Equals(other)
|
||||||
|
&& BpmMultiplier == other.BpmMultiplier;
|
||||||
|
|
||||||
|
// ReSharper disable once NonReadonlyMemberInGetHashCode
|
||||||
|
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), BpmMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class LegacySampleControlPoint : SampleControlPoint
|
internal class LegacySampleControlPoint : SampleControlPoint, IEquatable<LegacySampleControlPoint>
|
||||||
{
|
{
|
||||||
public int CustomSampleBank;
|
public int CustomSampleBank;
|
||||||
|
|
||||||
@ -204,7 +213,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
return baseInfo;
|
return baseInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsRedundant(ControlPoint existing)
|
public override bool IsRedundant(ControlPoint? existing)
|
||||||
=> base.IsRedundant(existing)
|
=> base.IsRedundant(existing)
|
||||||
&& existing is LegacySampleControlPoint existingSample
|
&& existing is LegacySampleControlPoint existingSample
|
||||||
&& CustomSampleBank == existingSample.CustomSampleBank;
|
&& CustomSampleBank == existingSample.CustomSampleBank;
|
||||||
@ -215,6 +224,17 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
CustomSampleBank = ((LegacySampleControlPoint)other).CustomSampleBank;
|
CustomSampleBank = ((LegacySampleControlPoint)other).CustomSampleBank;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Equals(ControlPoint? other)
|
||||||
|
=> other is LegacySampleControlPoint otherLegacySampleControlPoint
|
||||||
|
&& Equals(otherLegacySampleControlPoint);
|
||||||
|
|
||||||
|
public bool Equals(LegacySampleControlPoint? other)
|
||||||
|
=> base.Equals(other)
|
||||||
|
&& CustomSampleBank == other.CustomSampleBank;
|
||||||
|
|
||||||
|
// ReSharper disable once NonReadonlyMemberInGetHashCode
|
||||||
|
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), CustomSampleBank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
TimeSinceLastBeat = beatLength - TimeUntilNextBeat;
|
TimeSinceLastBeat = beatLength - TimeUntilNextBeat;
|
||||||
|
|
||||||
if (timingPoint == lastTimingPoint && beatIndex == lastBeat)
|
if (ReferenceEquals(timingPoint, lastTimingPoint) && beatIndex == lastBeat)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// as this event is sometimes used for sound triggers where `BeginDelayedSequence` has no effect, avoid firing it if too far away from the beat.
|
// as this event is sometimes used for sound triggers where `BeginDelayedSequence` has no effect, avoid firing it if too far away from the beat.
|
||||||
|
@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
|
|
||||||
if (legacyInfo != null)
|
if (legacyInfo != null)
|
||||||
DifficultyControlPoint = (DifficultyControlPoint)legacyInfo.DifficultyPointAt(StartTime).DeepClone();
|
DifficultyControlPoint = (DifficultyControlPoint)legacyInfo.DifficultyPointAt(StartTime).DeepClone();
|
||||||
else if (DifficultyControlPoint == DifficultyControlPoint.DEFAULT)
|
else if (ReferenceEquals(DifficultyControlPoint, DifficultyControlPoint.DEFAULT))
|
||||||
DifficultyControlPoint = new DifficultyControlPoint();
|
DifficultyControlPoint = new DifficultyControlPoint();
|
||||||
|
|
||||||
DifficultyControlPoint.Time = StartTime;
|
DifficultyControlPoint.Time = StartTime;
|
||||||
@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
// This is done here after ApplyDefaultsToSelf as we may require custom defaults to be applied to have an accurate end time.
|
// This is done here after ApplyDefaultsToSelf as we may require custom defaults to be applied to have an accurate end time.
|
||||||
if (legacyInfo != null)
|
if (legacyInfo != null)
|
||||||
SampleControlPoint = (SampleControlPoint)legacyInfo.SamplePointAt(this.GetEndTime() + control_point_leniency).DeepClone();
|
SampleControlPoint = (SampleControlPoint)legacyInfo.SamplePointAt(this.GetEndTime() + control_point_leniency).DeepClone();
|
||||||
else if (SampleControlPoint == SampleControlPoint.DEFAULT)
|
else if (ReferenceEquals(SampleControlPoint, SampleControlPoint.DEFAULT))
|
||||||
SampleControlPoint = new SampleControlPoint();
|
SampleControlPoint = new SampleControlPoint();
|
||||||
|
|
||||||
SampleControlPoint.Time = this.GetEndTime() + control_point_leniency;
|
SampleControlPoint.Time = this.GetEndTime() + control_point_leniency;
|
||||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
case NotifyCollectionChangedAction.Remove:
|
case NotifyCollectionChangedAction.Remove:
|
||||||
foreach (var group in args.OldItems.OfType<ControlPointGroup>())
|
foreach (var group in args.OldItems.OfType<ControlPointGroup>())
|
||||||
{
|
{
|
||||||
var matching = Children.SingleOrDefault(gv => gv.Group == group);
|
var matching = Children.SingleOrDefault(gv => ReferenceEquals(gv.Group, group));
|
||||||
|
|
||||||
if (matching != null)
|
if (matching != null)
|
||||||
matching.Expire();
|
matching.Expire();
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
case NotifyCollectionChangedAction.Remove:
|
case NotifyCollectionChangedAction.Remove:
|
||||||
foreach (var group in args.OldItems.OfType<ControlPointGroup>())
|
foreach (var group in args.OldItems.OfType<ControlPointGroup>())
|
||||||
{
|
{
|
||||||
var matching = Children.SingleOrDefault(gv => gv.Group == group);
|
var matching = Children.SingleOrDefault(gv => ReferenceEquals(gv.Group, group));
|
||||||
|
|
||||||
matching?.Expire();
|
matching?.Expire();
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
updateRepeats(repeats);
|
updateRepeats(repeats);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (difficultyControlPoint != Item.DifficultyControlPoint)
|
if (!ReferenceEquals(difficultyControlPoint, Item.DifficultyControlPoint))
|
||||||
{
|
{
|
||||||
difficultyControlPoint = Item.DifficultyControlPoint;
|
difficultyControlPoint = Item.DifficultyControlPoint;
|
||||||
difficultyOverrideDisplay?.Expire();
|
difficultyOverrideDisplay?.Expire();
|
||||||
@ -220,7 +220,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sampleControlPoint != Item.SampleControlPoint)
|
if (!ReferenceEquals(sampleControlPoint, Item.SampleControlPoint))
|
||||||
{
|
{
|
||||||
sampleControlPoint = Item.SampleControlPoint;
|
sampleControlPoint = Item.SampleControlPoint;
|
||||||
sampleOverrideDisplay?.Expire();
|
sampleOverrideDisplay?.Expire();
|
||||||
@ -393,7 +393,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
|
|
||||||
if (e.CurrentState.Keyboard.ShiftPressed)
|
if (e.CurrentState.Keyboard.ShiftPressed)
|
||||||
{
|
{
|
||||||
if (hitObject.DifficultyControlPoint == DifficultyControlPoint.DEFAULT)
|
if (ReferenceEquals(hitObject.DifficultyControlPoint, DifficultyControlPoint.DEFAULT))
|
||||||
hitObject.DifficultyControlPoint = new DifficultyControlPoint();
|
hitObject.DifficultyControlPoint = new DifficultyControlPoint();
|
||||||
|
|
||||||
double newVelocity = hitObject.DifficultyControlPoint.SliderVelocity * (repeatHitObject.Duration / proposedDuration);
|
double newVelocity = hitObject.DifficultyControlPoint.SliderVelocity * (repeatHitObject.Duration / proposedDuration);
|
||||||
|
@ -73,31 +73,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
public EditorBeatmap(IBeatmap playableBeatmap, ISkin beatmapSkin = null, BeatmapInfo beatmapInfo = null)
|
public EditorBeatmap(IBeatmap playableBeatmap, ISkin beatmapSkin = null, BeatmapInfo beatmapInfo = null)
|
||||||
{
|
{
|
||||||
PlayableBeatmap = playableBeatmap;
|
PlayableBeatmap = playableBeatmap;
|
||||||
|
PlayableBeatmap.ControlPointInfo = ConvertControlPoints(PlayableBeatmap.ControlPointInfo);
|
||||||
// ensure we are not working with legacy control points.
|
|
||||||
// if we leave the legacy points around they will be applied over any local changes on
|
|
||||||
// ApplyDefaults calls. this should eventually be removed once the default logic is moved to the decoder/converter.
|
|
||||||
if (PlayableBeatmap.ControlPointInfo is LegacyControlPointInfo)
|
|
||||||
{
|
|
||||||
var newControlPoints = new ControlPointInfo();
|
|
||||||
|
|
||||||
foreach (var controlPoint in PlayableBeatmap.ControlPointInfo.AllControlPoints)
|
|
||||||
{
|
|
||||||
switch (controlPoint)
|
|
||||||
{
|
|
||||||
case DifficultyControlPoint _:
|
|
||||||
case SampleControlPoint _:
|
|
||||||
// skip legacy types.
|
|
||||||
continue;
|
|
||||||
|
|
||||||
default:
|
|
||||||
newControlPoints.Add(controlPoint.Time, controlPoint);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
playableBeatmap.ControlPointInfo = newControlPoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.beatmapInfo = beatmapInfo ?? playableBeatmap.BeatmapInfo;
|
this.beatmapInfo = beatmapInfo ?? playableBeatmap.BeatmapInfo;
|
||||||
|
|
||||||
@ -110,6 +86,39 @@ namespace osu.Game.Screens.Edit
|
|||||||
trackStartTime(obj);
|
trackStartTime(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a <see cref="ControlPointInfo"/> such that the resultant <see cref="ControlPointInfo"/> is non-legacy.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="incoming">The <see cref="ControlPointInfo"/> to convert.</param>
|
||||||
|
/// <returns>The non-legacy <see cref="ControlPointInfo"/>. <paramref name="incoming"/> is returned if already non-legacy.</returns>
|
||||||
|
public static ControlPointInfo ConvertControlPoints(ControlPointInfo incoming)
|
||||||
|
{
|
||||||
|
// ensure we are not working with legacy control points.
|
||||||
|
// if we leave the legacy points around they will be applied over any local changes on
|
||||||
|
// ApplyDefaults calls. this should eventually be removed once the default logic is moved to the decoder/converter.
|
||||||
|
if (!(incoming is LegacyControlPointInfo))
|
||||||
|
return incoming;
|
||||||
|
|
||||||
|
var newControlPoints = new ControlPointInfo();
|
||||||
|
|
||||||
|
foreach (var controlPoint in incoming.AllControlPoints)
|
||||||
|
{
|
||||||
|
switch (controlPoint)
|
||||||
|
{
|
||||||
|
case DifficultyControlPoint _:
|
||||||
|
case SampleControlPoint _:
|
||||||
|
// skip legacy types.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
default:
|
||||||
|
newControlPoints.Add(controlPoint.Time, controlPoint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newControlPoints;
|
||||||
|
}
|
||||||
|
|
||||||
public BeatmapInfo BeatmapInfo
|
public BeatmapInfo BeatmapInfo
|
||||||
{
|
{
|
||||||
get => beatmapInfo;
|
get => beatmapInfo;
|
||||||
|
@ -141,7 +141,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
seekTime = timingPoint.Time + closestBeat * seekAmount;
|
seekTime = timingPoint.Time + closestBeat * seekAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seekTime < timingPoint.Time && timingPoint != ControlPointInfo.TimingPoints.First())
|
if (seekTime < timingPoint.Time && !ReferenceEquals(timingPoint, ControlPointInfo.TimingPoints.First()))
|
||||||
seekTime = timingPoint.Time;
|
seekTime = timingPoint.Time;
|
||||||
|
|
||||||
SeekSmoothlyTo(seekTime);
|
SeekSmoothlyTo(seekTime);
|
||||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OverlayColourProvider colours)
|
private void load(OverlayColourProvider colours)
|
||||||
{
|
{
|
||||||
hoveredBackground.Colour = colourHover = colours.Background1;
|
colourHover = colours.Background1;
|
||||||
colourSelected = colours.Colour3;
|
colourSelected = colours.Colour3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,8 +105,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
// Reduce flicker of rows when offset is being changed rapidly.
|
updateState();
|
||||||
// Probably need to reconsider this.
|
|
||||||
FinishTransforms(true);
|
FinishTransforms(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,13 +5,16 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using DiffPlex;
|
using DiffPlex;
|
||||||
|
using DiffPlex.Model;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
|
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
|
||||||
@ -34,61 +37,107 @@ namespace osu.Game.Screens.Edit
|
|||||||
{
|
{
|
||||||
// Diff the beatmaps
|
// Diff the beatmaps
|
||||||
var result = new Differ().CreateLineDiffs(readString(currentState), readString(newState), true, false);
|
var result = new Differ().CreateLineDiffs(readString(currentState), readString(newState), true, false);
|
||||||
|
IBeatmap newBeatmap = null;
|
||||||
|
|
||||||
// Find the index of [HitObject] sections. Lines changed prior to this index are ignored.
|
editorBeatmap.BeginChange();
|
||||||
int oldHitObjectsIndex = Array.IndexOf(result.PiecesOld, "[HitObjects]");
|
processHitObjects(result, () => newBeatmap ??= readBeatmap(newState));
|
||||||
int newHitObjectsIndex = Array.IndexOf(result.PiecesNew, "[HitObjects]");
|
processTimingPoints(() => newBeatmap ??= readBeatmap(newState));
|
||||||
|
editorBeatmap.EndChange();
|
||||||
|
}
|
||||||
|
|
||||||
Debug.Assert(oldHitObjectsIndex >= 0);
|
private void processTimingPoints(Func<IBeatmap> getNewBeatmap)
|
||||||
Debug.Assert(newHitObjectsIndex >= 0);
|
{
|
||||||
|
ControlPointInfo newControlPoints = EditorBeatmap.ConvertControlPoints(getNewBeatmap().ControlPointInfo);
|
||||||
|
|
||||||
var toRemove = new List<int>();
|
// Remove all groups from the current beatmap which don't have a corresponding equal group in the new beatmap.
|
||||||
var toAdd = new List<int>();
|
foreach (var oldGroup in editorBeatmap.ControlPointInfo.Groups.ToArray())
|
||||||
|
{
|
||||||
|
var newGroup = newControlPoints.GroupAt(oldGroup.Time);
|
||||||
|
|
||||||
|
if (!oldGroup.Equals(newGroup))
|
||||||
|
editorBeatmap.ControlPointInfo.RemoveGroup(oldGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all groups from the new beatmap which don't have a corresponding equal group in the old beatmap.
|
||||||
|
foreach (var newGroup in newControlPoints.Groups)
|
||||||
|
{
|
||||||
|
var oldGroup = editorBeatmap.ControlPointInfo.GroupAt(newGroup.Time);
|
||||||
|
|
||||||
|
if (!newGroup.Equals(oldGroup))
|
||||||
|
{
|
||||||
|
foreach (var point in newGroup.ControlPoints)
|
||||||
|
editorBeatmap.ControlPointInfo.Add(newGroup.Time, point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processHitObjects(DiffResult result, Func<IBeatmap> getNewBeatmap)
|
||||||
|
{
|
||||||
|
findChangedIndices(result, LegacyDecoder<Beatmap>.Section.HitObjects, out var removedIndices, out var addedIndices);
|
||||||
|
|
||||||
|
for (int i = removedIndices.Count - 1; i >= 0; i--)
|
||||||
|
editorBeatmap.RemoveAt(removedIndices[i]);
|
||||||
|
|
||||||
|
if (addedIndices.Count > 0)
|
||||||
|
{
|
||||||
|
var newBeatmap = getNewBeatmap();
|
||||||
|
|
||||||
|
foreach (int i in addedIndices)
|
||||||
|
editorBeatmap.Insert(i, newBeatmap.HitObjects[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findChangedIndices(DiffResult result, LegacyDecoder<Beatmap>.Section section, out List<int> removedIndices, out List<int> addedIndices)
|
||||||
|
{
|
||||||
|
removedIndices = new List<int>();
|
||||||
|
addedIndices = new List<int>();
|
||||||
|
|
||||||
|
// Find the start and end indices of the relevant section headers in both the old and the new beatmap file. Lines changed outside of the modified ranges are ignored.
|
||||||
|
int oldSectionStartIndex = Array.IndexOf(result.PiecesOld, $"[{section}]");
|
||||||
|
if (oldSectionStartIndex == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int oldSectionEndIndex = Array.FindIndex(result.PiecesOld, oldSectionStartIndex + 1, s => s.StartsWith('['));
|
||||||
|
if (oldSectionEndIndex == -1)
|
||||||
|
oldSectionEndIndex = result.PiecesOld.Length;
|
||||||
|
|
||||||
|
int newSectionStartIndex = Array.IndexOf(result.PiecesNew, $"[{section}]");
|
||||||
|
if (newSectionStartIndex == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int newSectionEndIndex = Array.FindIndex(result.PiecesNew, newSectionStartIndex + 1, s => s.StartsWith('['));
|
||||||
|
if (newSectionEndIndex == -1)
|
||||||
|
newSectionEndIndex = result.PiecesNew.Length;
|
||||||
|
|
||||||
foreach (var block in result.DiffBlocks)
|
foreach (var block in result.DiffBlocks)
|
||||||
{
|
{
|
||||||
// Removed hitobjects
|
// Removed indices
|
||||||
for (int i = 0; i < block.DeleteCountA; i++)
|
for (int i = 0; i < block.DeleteCountA; i++)
|
||||||
{
|
{
|
||||||
int hoIndex = block.DeleteStartA + i - oldHitObjectsIndex - 1;
|
int objectIndex = block.DeleteStartA + i;
|
||||||
|
|
||||||
if (hoIndex < 0)
|
if (objectIndex <= oldSectionStartIndex || objectIndex >= oldSectionEndIndex)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
toRemove.Add(hoIndex);
|
removedIndices.Add(objectIndex - oldSectionStartIndex - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Added hitobjects
|
// Added indices
|
||||||
for (int i = 0; i < block.InsertCountB; i++)
|
for (int i = 0; i < block.InsertCountB; i++)
|
||||||
{
|
{
|
||||||
int hoIndex = block.InsertStartB + i - newHitObjectsIndex - 1;
|
int objectIndex = block.InsertStartB + i;
|
||||||
|
|
||||||
if (hoIndex < 0)
|
if (objectIndex <= newSectionStartIndex || objectIndex >= newSectionEndIndex)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
toAdd.Add(hoIndex);
|
addedIndices.Add(objectIndex - newSectionStartIndex - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the indices to ensure that removal + insertion indices don't get jumbled up post-removal or post-insertion.
|
// Sort the indices to ensure that removal + insertion indices don't get jumbled up post-removal or post-insertion.
|
||||||
// This isn't strictly required, but the differ makes no guarantees about order.
|
// This isn't strictly required, but the differ makes no guarantees about order.
|
||||||
toRemove.Sort();
|
removedIndices.Sort();
|
||||||
toAdd.Sort();
|
addedIndices.Sort();
|
||||||
|
|
||||||
editorBeatmap.BeginChange();
|
|
||||||
|
|
||||||
// Apply the changes.
|
|
||||||
for (int i = toRemove.Count - 1; i >= 0; i--)
|
|
||||||
editorBeatmap.RemoveAt(toRemove[i]);
|
|
||||||
|
|
||||||
if (toAdd.Count > 0)
|
|
||||||
{
|
|
||||||
IBeatmap newBeatmap = readBeatmap(newState);
|
|
||||||
foreach (int i in toAdd)
|
|
||||||
editorBeatmap.Insert(i, newBeatmap.HitObjects[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
editorBeatmap.EndChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string readString(byte[] state) => Encoding.UTF8.GetString(state);
|
private string readString(byte[] state) => Encoding.UTF8.GetString(state);
|
||||||
|
@ -54,6 +54,8 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
|
|
||||||
Columns = createHeaders();
|
Columns = createHeaders();
|
||||||
Content = value.Select(createContent).ToArray().ToRectangular();
|
Content = value.Select(createContent).ToArray().ToRectangular();
|
||||||
|
|
||||||
|
updateSelectedGroup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,10 +66,17 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
selectedGroup.BindValueChanged(group =>
|
selectedGroup.BindValueChanged(group =>
|
||||||
{
|
{
|
||||||
// TODO: This should scroll the selected row into view.
|
// TODO: This should scroll the selected row into view.
|
||||||
foreach (var b in BackgroundFlow) b.Selected = b.Item == group.NewValue;
|
updateSelectedGroup();
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateSelectedGroup()
|
||||||
|
{
|
||||||
|
// TODO: This should scroll the selected row into view.
|
||||||
|
foreach (var b in BackgroundFlow)
|
||||||
|
b.Selected = ReferenceEquals(b.Item, selectedGroup?.Value);
|
||||||
|
}
|
||||||
|
|
||||||
private TableColumn[] createHeaders()
|
private TableColumn[] createHeaders()
|
||||||
{
|
{
|
||||||
var columns = new List<TableColumn>
|
var columns = new List<TableColumn>
|
||||||
|
@ -222,7 +222,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
// Try and create matching types from the currently selected control point.
|
// Try and create matching types from the currently selected control point.
|
||||||
var selected = selectedGroup.Value;
|
var selected = selectedGroup.Value;
|
||||||
|
|
||||||
if (selected != null && selected != group)
|
if (selected != null && !ReferenceEquals(selected, group))
|
||||||
{
|
{
|
||||||
foreach (var controlPoint in selected.ControlPoints)
|
foreach (var controlPoint in selected.ControlPoints)
|
||||||
{
|
{
|
||||||
|
@ -128,7 +128,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
double? offsetChange = newStartTime - selectedGroupStartTime;
|
double? offsetChange = newStartTime - selectedGroupStartTime;
|
||||||
|
|
||||||
var nextGroup = editorBeatmap.ControlPointInfo.TimingPoints
|
var nextGroup = editorBeatmap.ControlPointInfo.TimingPoints
|
||||||
.SkipWhile(g => g != tcp)
|
.SkipWhile(g => !ReferenceEquals(g, tcp))
|
||||||
.Skip(1)
|
.Skip(1)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user