1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 17:27:24 +08:00

Merge pull request #28444 from bdach/scrolling-ruleset-editor-reloads

Reload scrolling hitobject composer on control point changes
This commit is contained in:
Dean Herbert 2024-06-20 15:15:26 +09:00 committed by GitHub
commit 0eb533f2fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 90 additions and 1 deletions

View File

@ -13,8 +13,28 @@ namespace osu.Game.Beatmaps.ControlPoints
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
public abstract class ControlPoint : IComparable<ControlPoint>, IDeepCloneable<ControlPoint>, IEquatable<ControlPoint>, IControlPoint 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] [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; public void AttachGroup(ControlPointGroup pointGroup) => Time = pointGroup.Time;

View File

@ -10,8 +10,11 @@ namespace osu.Game.Beatmaps.ControlPoints
public class ControlPointGroup : IComparable<ControlPointGroup>, IEquatable<ControlPointGroup> public class ControlPointGroup : IComparable<ControlPointGroup>, IEquatable<ControlPointGroup>
{ {
public event Action<ControlPoint>? ItemAdded; public event Action<ControlPoint>? ItemAdded;
public event Action<ControlPoint>? ItemChanged;
public event Action<ControlPoint>? ItemRemoved; public event Action<ControlPoint>? ItemRemoved;
private void raiseItemChanged(ControlPoint controlPoint) => ItemChanged?.Invoke(controlPoint);
/// <summary> /// <summary>
/// The time at which the control point takes effect. /// The time at which the control point takes effect.
/// </summary> /// </summary>
@ -39,12 +42,14 @@ namespace osu.Game.Beatmaps.ControlPoints
controlPoints.Add(point); controlPoints.Add(point);
ItemAdded?.Invoke(point); ItemAdded?.Invoke(point);
point.Changed += raiseItemChanged;
} }
public void Remove(ControlPoint point) public void Remove(ControlPoint point)
{ {
controlPoints.Remove(point); controlPoints.Remove(point);
ItemRemoved?.Invoke(point); ItemRemoved?.Invoke(point);
point.Changed -= raiseItemChanged;
} }
public sealed override bool Equals(object? obj) public sealed override bool Equals(object? obj)

View File

@ -20,6 +20,14 @@ namespace osu.Game.Beatmaps.ControlPoints
[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
public class ControlPointInfo : IDeepCloneable<ControlPointInfo> 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> /// <summary>
/// All control points grouped by time. /// All control points grouped by time.
/// </summary> /// </summary>
@ -117,6 +125,7 @@ namespace osu.Game.Beatmaps.ControlPoints
if (addIfNotExisting) if (addIfNotExisting)
{ {
newGroup.ItemAdded += GroupItemAdded; newGroup.ItemAdded += GroupItemAdded;
newGroup.ItemChanged += raiseControlPointsChanged;
newGroup.ItemRemoved += GroupItemRemoved; newGroup.ItemRemoved += GroupItemRemoved;
groups.Insert(~i, newGroup); groups.Insert(~i, newGroup);
@ -132,6 +141,7 @@ namespace osu.Game.Beatmaps.ControlPoints
group.Remove(item); group.Remove(item);
group.ItemAdded -= GroupItemAdded; group.ItemAdded -= GroupItemAdded;
group.ItemChanged -= raiseControlPointsChanged;
group.ItemRemoved -= GroupItemRemoved; group.ItemRemoved -= GroupItemRemoved;
groups.Remove(group); groups.Remove(group);
@ -288,6 +298,8 @@ namespace osu.Game.Beatmaps.ControlPoints
default: default:
throw new ArgumentException($"A control point of unexpected type {controlPoint.GetType()} was added to this {nameof(ControlPointInfo)}"); throw new ArgumentException($"A control point of unexpected type {controlPoint.GetType()} was added to this {nameof(ControlPointInfo)}");
} }
raiseControlPointsChanged();
} }
protected virtual void GroupItemRemoved(ControlPoint controlPoint) protected virtual void GroupItemRemoved(ControlPoint controlPoint)
@ -302,6 +314,8 @@ namespace osu.Game.Beatmaps.ControlPoints
effectPoints.Remove(typed); effectPoints.Remove(typed);
break; break;
} }
raiseControlPointsChanged();
} }
public ControlPointInfo DeepClone() public ControlPointInfo DeepClone()

View File

@ -44,6 +44,11 @@ namespace osu.Game.Beatmaps.ControlPoints
set => SliderVelocityBindable.Value = value; set => SliderVelocityBindable.Value = value;
} }
public DifficultyControlPoint()
{
SliderVelocityBindable.BindValueChanged(_ => RaiseChanged());
}
public override bool IsRedundant(ControlPoint? existing) public override bool IsRedundant(ControlPoint? existing)
=> existing is DifficultyControlPoint existingDifficulty => existing is DifficultyControlPoint existingDifficulty
&& GenerateTicks == existingDifficulty.GenerateTicks && GenerateTicks == existingDifficulty.GenerateTicks

View File

@ -50,6 +50,12 @@ namespace osu.Game.Beatmaps.ControlPoints
set => KiaiModeBindable.Value = value; set => KiaiModeBindable.Value = value;
} }
public EffectControlPoint()
{
KiaiModeBindable.BindValueChanged(_ => RaiseChanged());
ScrollSpeedBindable.BindValueChanged(_ => RaiseChanged());
}
public override bool IsRedundant(ControlPoint? existing) public override bool IsRedundant(ControlPoint? existing)
=> existing is EffectControlPoint existingEffect => existing is EffectControlPoint existingEffect
&& KiaiMode == existingEffect.KiaiMode && KiaiMode == existingEffect.KiaiMode

View File

@ -56,6 +56,12 @@ namespace osu.Game.Beatmaps.ControlPoints
set => SampleVolumeBindable.Value = value; set => SampleVolumeBindable.Value = value;
} }
public SampleControlPoint()
{
SampleBankBindable.BindValueChanged(_ => RaiseChanged());
SampleVolumeBindable.BindValueChanged(_ => RaiseChanged());
}
/// <summary> /// <summary>
/// Create a SampleInfo based on the sample settings in this control point. /// Create a SampleInfo based on the sample settings in this control point.
/// </summary> /// </summary>

View File

@ -82,6 +82,13 @@ namespace osu.Game.Beatmaps.ControlPoints
/// </summary> /// </summary>
public double BPM => 60000 / BeatLength; 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. // 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;

View File

@ -4,6 +4,7 @@
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
@ -12,6 +13,7 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Components.TernaryButtons;
using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components;
using osuTK; using osuTK;
@ -21,6 +23,9 @@ namespace osu.Game.Rulesets.Edit
public abstract partial class ScrollingHitObjectComposer<TObject> : HitObjectComposer<TObject> public abstract partial class ScrollingHitObjectComposer<TObject> : HitObjectComposer<TObject>
where TObject : HitObject where TObject : HitObject
{ {
[Resolved]
private Editor? editor { get; set; }
private readonly Bindable<TernaryState> showSpeedChanges = new Bindable<TernaryState>(); private readonly Bindable<TernaryState> showSpeedChanges = new Bindable<TernaryState>();
private Bindable<bool> configShowSpeedChanges = null!; private Bindable<bool> configShowSpeedChanges = null!;
@ -72,6 +77,8 @@ namespace osu.Game.Rulesets.Edit
if (beatSnapGrid != null) if (beatSnapGrid != null)
AddInternal(beatSnapGrid); AddInternal(beatSnapGrid);
EditorBeatmap.ControlPointInfo.ControlPointsChanged += expireComposeScreenOnControlPointChange;
} }
protected override void UpdateAfterChildren() protected override void UpdateAfterChildren()
@ -104,5 +111,15 @@ namespace osu.Game.Rulesets.Edit
beatSnapGrid.SelectionTimeRange = null; beatSnapGrid.SelectionTimeRange = null;
} }
} }
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (EditorBeatmap.IsNotNull())
EditorBeatmap.ControlPointInfo.ControlPointsChanged -= expireComposeScreenOnControlPointChange;
}
private void expireComposeScreenOnControlPointChange() => editor?.ReloadComposeScreen();
} }
} }

View File

@ -1019,6 +1019,15 @@ namespace osu.Game.Screens.Edit
} }
} }
/// <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] [CanBeNull]
private ScheduledDelegate playbackDisabledDebounce; private ScheduledDelegate playbackDisabledDebounce;