Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion osu.Game/Beatmaps/ControlPoints/ControlPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,28 @@ namespace osu.Game.Beatmaps.ControlPoints
{
public abstract class ControlPoint : IComparable<ControlPoint>, IDeepCloneable<ControlPoint>, IEquatable<ControlPoint>, IControlPoint
{
/// <summary>
/// Invoked when any of this <see cref="ControlPoint"/>'s properties have changed.
/// </summary>
public event Action<ControlPoint>? Changed;

protected void RaiseChanged() => Changed?.Invoke(this);

private double time;

[JsonIgnore]
public double Time { get; set; }
public double Time
{
get => time;
set
{
if (time == value)
return;

time = value;
RaiseChanged();
}
}

public void AttachGroup(ControlPointGroup pointGroup) => Time = pointGroup.Time;

Expand Down
5 changes: 5 additions & 0 deletions osu.Game/Beatmaps/ControlPoints/ControlPointGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ namespace osu.Game.Beatmaps.ControlPoints
public class ControlPointGroup : IComparable<ControlPointGroup>, IEquatable<ControlPointGroup>
{
public event Action<ControlPoint>? ItemAdded;
public event Action<ControlPoint>? ItemChanged;
public event Action<ControlPoint>? ItemRemoved;

private void raiseItemChanged(ControlPoint controlPoint) => ItemChanged?.Invoke(controlPoint);

/// <summary>
/// The time at which the control point takes effect.
/// </summary>
Expand Down Expand Up @@ -39,12 +42,14 @@ public void Add(ControlPoint point)

controlPoints.Add(point);
ItemAdded?.Invoke(point);
point.Changed += raiseItemChanged;
}

public void Remove(ControlPoint point)
{
controlPoints.Remove(point);
ItemRemoved?.Invoke(point);
point.Changed -= raiseItemChanged;
}

public sealed override bool Equals(object? obj)
Expand Down
14 changes: 14 additions & 0 deletions osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ namespace osu.Game.Beatmaps.ControlPoints
[Serializable]
public class ControlPointInfo : IDeepCloneable<ControlPointInfo>
{
/// <summary>
/// Invoked on any change to the set of control points.
/// </summary>
[CanBeNull]
public event Action ControlPointsChanged;

private void raiseControlPointsChanged([CanBeNull] ControlPoint _ = null) => ControlPointsChanged?.Invoke();

/// <summary>
/// All control points grouped by time.
/// </summary>
Expand Down Expand Up @@ -116,6 +124,7 @@ public ControlPointGroup GroupAt(double time, bool addIfNotExisting = false)
if (addIfNotExisting)
{
newGroup.ItemAdded += GroupItemAdded;
newGroup.ItemChanged += raiseControlPointsChanged;
newGroup.ItemRemoved += GroupItemRemoved;

groups.Insert(~i, newGroup);
Expand All @@ -131,6 +140,7 @@ public void RemoveGroup(ControlPointGroup group)
group.Remove(item);

group.ItemAdded -= GroupItemAdded;
group.ItemChanged -= raiseControlPointsChanged;
group.ItemRemoved -= GroupItemRemoved;

groups.Remove(group);
Expand Down Expand Up @@ -287,6 +297,8 @@ protected virtual void GroupItemAdded(ControlPoint controlPoint)
default:
throw new ArgumentException($"A control point of unexpected type {controlPoint.GetType()} was added to this {nameof(ControlPointInfo)}");
}

raiseControlPointsChanged();
}

protected virtual void GroupItemRemoved(ControlPoint controlPoint)
Expand All @@ -301,6 +313,8 @@ protected virtual void GroupItemRemoved(ControlPoint controlPoint)
effectPoints.Remove(typed);
break;
}

raiseControlPointsChanged();
}

public ControlPointInfo DeepClone()
Expand Down
5 changes: 5 additions & 0 deletions osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public double SliderVelocity
set => SliderVelocityBindable.Value = value;
}

public DifficultyControlPoint()
{
SliderVelocityBindable.BindValueChanged(_ => RaiseChanged());
}

public override bool IsRedundant(ControlPoint? existing)
=> existing is DifficultyControlPoint existingDifficulty
&& GenerateTicks == existingDifficulty.GenerateTicks
Expand Down
6 changes: 6 additions & 0 deletions osu.Game/Beatmaps/ControlPoints/EffectControlPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ public bool KiaiMode
set => KiaiModeBindable.Value = value;
}

public EffectControlPoint()
{
KiaiModeBindable.BindValueChanged(_ => RaiseChanged());
ScrollSpeedBindable.BindValueChanged(_ => RaiseChanged());
}

public override bool IsRedundant(ControlPoint? existing)
=> existing is EffectControlPoint existingEffect
&& KiaiMode == existingEffect.KiaiMode
Expand Down
6 changes: 6 additions & 0 deletions osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ public int SampleVolume
set => SampleVolumeBindable.Value = value;
}

public SampleControlPoint()
{
SampleBankBindable.BindValueChanged(_ => RaiseChanged());
SampleVolumeBindable.BindValueChanged(_ => RaiseChanged());
}

/// <summary>
/// Create a SampleInfo based on the sample settings in this control point.
/// </summary>
Expand Down
7 changes: 7 additions & 0 deletions osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ public double BeatLength
/// </summary>
public double BPM => 60000 / BeatLength;

public TimingControlPoint()
{
TimeSignatureBindable.BindValueChanged(_ => RaiseChanged());
OmitFirstBarLineBindable.BindValueChanged(_ => RaiseChanged());
BeatLengthBindable.BindValueChanged(_ => RaiseChanged());
}

// Timing points are never redundant as they can change the time signature.
public override bool IsRedundant(ControlPoint? existing) => false;

Expand Down
17 changes: 17 additions & 0 deletions osu.Game/Rulesets/Edit/ScrollingHitObjectComposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
Expand All @@ -12,6 +13,7 @@
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.TernaryButtons;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
Expand All @@ -21,6 +23,9 @@ namespace osu.Game.Rulesets.Edit
public abstract partial class ScrollingHitObjectComposer<TObject> : HitObjectComposer<TObject>
where TObject : HitObject
{
[Resolved]
private Editor? editor { get; set; }

private readonly Bindable<TernaryState> showSpeedChanges = new Bindable<TernaryState>();
private Bindable<bool> configShowSpeedChanges = null!;

Expand Down Expand Up @@ -72,6 +77,8 @@ private void load(OsuConfigManager config)

if (beatSnapGrid != null)
AddInternal(beatSnapGrid);

EditorBeatmap.ControlPointInfo.ControlPointsChanged += expireComposeScreenOnControlPointChange;
}

protected override void UpdateAfterChildren()
Expand Down Expand Up @@ -104,5 +111,15 @@ private void updateBeatSnapGrid()
beatSnapGrid.SelectionTimeRange = null;
}
}

protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);

if (EditorBeatmap.IsNotNull())
EditorBeatmap.ControlPointInfo.ControlPointsChanged -= expireComposeScreenOnControlPointChange;
}

private void expireComposeScreenOnControlPointChange() => editor?.ReloadComposeScreen();
}
}
9 changes: 9 additions & 0 deletions osu.Game/Screens/Edit/Editor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,15 @@ private void onModeChanged(ValueChangedEvent<EditorScreenMode> e)
}
}

/// <summary>
/// Forces a reload of the compose screen after significant configuration changes.
/// </summary>
/// <remarks>
/// This can be necessary for scrolling rulesets, as they do not easily support control points changing under them.
/// The reason that this works is that <see cref="onModeChanged"/> will re-instantiate the screen whenever it is requested next.
/// </remarks>
public void ReloadComposeScreen() => screenContainer.SingleOrDefault(s => s.Type == EditorScreenMode.Compose)?.RemoveAndDisposeImmediately();

[CanBeNull]
private ScheduledDelegate playbackDisabledDebounce;

Expand Down