mirror of
https://github.com/ppy/osu.git
synced 2025-02-20 23:23:30 +08:00
Add the ability to add new ControlPoint types to existing groups
This commit is contained in:
parent
de69665a46
commit
0fba272e78
@ -17,6 +17,11 @@ namespace osu.Game.Tests.Visual.Editor
|
||||
{
|
||||
typeof(ControlPointTable),
|
||||
typeof(ControlPointSettings),
|
||||
typeof(Section<>),
|
||||
typeof(TimingSection),
|
||||
typeof(EffectSection),
|
||||
typeof(SampleSection),
|
||||
typeof(DifficultySection),
|
||||
typeof(RowAttribute)
|
||||
};
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
|
||||
namespace osu.Game.Beatmaps.ControlPoints
|
||||
{
|
||||
@ -17,9 +17,9 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
/// </summary>
|
||||
public double Time { get; }
|
||||
|
||||
public IReadOnlyList<ControlPoint> ControlPoints => controlPoints;
|
||||
public IBindableList<ControlPoint> ControlPoints => controlPoints;
|
||||
|
||||
private readonly List<ControlPoint> controlPoints = new List<ControlPoint>();
|
||||
private readonly BindableList<ControlPoint> controlPoints = new BindableList<ControlPoint>();
|
||||
|
||||
public ControlPointGroup(double time)
|
||||
{
|
||||
|
@ -2,17 +2,12 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Timing
|
||||
{
|
||||
@ -51,190 +46,5 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
new SampleSection(),
|
||||
new EffectSection(),
|
||||
};
|
||||
|
||||
private class TimingSection : Section<TimingControlPoint>
|
||||
{
|
||||
private OsuSpriteText bpm;
|
||||
private OsuSpriteText timeSignature;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Flow.AddRange(new[]
|
||||
{
|
||||
bpm = new OsuSpriteText(),
|
||||
timeSignature = new OsuSpriteText(),
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ControlPoint.BindValueChanged(point =>
|
||||
{
|
||||
bpm.Text = $"BPM: {point.NewValue?.BeatLength}";
|
||||
timeSignature.Text = $"Signature: {point.NewValue?.TimeSignature}";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class DifficultySection : Section<DifficultyControlPoint>
|
||||
{
|
||||
private OsuSpriteText multiplier;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Flow.AddRange(new[]
|
||||
{
|
||||
multiplier = new OsuSpriteText(),
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ControlPoint.BindValueChanged(point => { multiplier.Text = $"Multiplier: {point.NewValue?.SpeedMultiplier}"; });
|
||||
}
|
||||
}
|
||||
|
||||
private class SampleSection : Section<SampleControlPoint>
|
||||
{
|
||||
private OsuSpriteText bank;
|
||||
private OsuSpriteText volume;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Flow.AddRange(new[]
|
||||
{
|
||||
bank = new OsuSpriteText(),
|
||||
volume = new OsuSpriteText(),
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ControlPoint.BindValueChanged(point =>
|
||||
{
|
||||
bank.Text = $"Bank: {point.NewValue?.SampleBank}";
|
||||
volume.Text = $"Volume: {point.NewValue?.SampleVolume}";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class EffectSection : Section<EffectControlPoint>
|
||||
{
|
||||
private OsuSpriteText kiai;
|
||||
private OsuSpriteText omitBarLine;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Flow.AddRange(new[]
|
||||
{
|
||||
kiai = new OsuSpriteText(),
|
||||
omitBarLine = new OsuSpriteText(),
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ControlPoint.BindValueChanged(point =>
|
||||
{
|
||||
kiai.Text = $"Kiai: {point.NewValue?.KiaiMode}";
|
||||
omitBarLine.Text = $"Skip Bar Line: {point.NewValue?.OmitFirstBarLine}";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class Section<T> : CompositeDrawable
|
||||
where T : ControlPoint
|
||||
{
|
||||
private OsuCheckbox checkbox;
|
||||
private Container content;
|
||||
|
||||
protected FillFlowContainer Flow { get; private set; }
|
||||
|
||||
protected Bindable<T> ControlPoint { get; } = new Bindable<T>();
|
||||
|
||||
private const float header_height = 20;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<ControlPointGroup> selectedPoints { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeDuration = 200;
|
||||
AutoSizeEasing = Easing.OutQuint;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Masking = true;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colours.Gray1,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = header_height,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
checkbox = new OsuCheckbox
|
||||
{
|
||||
LabelText = typeof(T).Name.Replace(typeof(ControlPoint).Name, string.Empty)
|
||||
}
|
||||
}
|
||||
},
|
||||
content = new Container
|
||||
{
|
||||
Y = header_height,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colours.Gray2,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
Flow = new FillFlowContainer
|
||||
{
|
||||
Padding = new MarginPadding(10),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
selectedPoints.BindValueChanged(points =>
|
||||
{
|
||||
ControlPoint.Value = points.NewValue?.ControlPoints.OfType<T>().FirstOrDefault();
|
||||
|
||||
checkbox.Current.Value = ControlPoint.Value != null;
|
||||
}, true);
|
||||
|
||||
checkbox.Current.BindValueChanged(selected => { content.BypassAutoSizeAxes = selected.NewValue ? Axes.None : Axes.Y; }, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,35 +92,57 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
Text = $"{group.Time:n0}ms",
|
||||
Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold)
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
ChildrenEnumerable = group.ControlPoints.Select(createAttribute).Where(c => c != null),
|
||||
Padding = new MarginPadding(10),
|
||||
Spacing = new Vector2(2)
|
||||
},
|
||||
new ControlGroupAttributes(group),
|
||||
};
|
||||
|
||||
private Drawable createAttribute(ControlPoint controlPoint)
|
||||
private class ControlGroupAttributes : CompositeDrawable
|
||||
{
|
||||
switch (controlPoint)
|
||||
private readonly IBindableList<ControlPoint> controlPoints;
|
||||
|
||||
private readonly FillFlowContainer fill;
|
||||
|
||||
public ControlGroupAttributes(ControlPointGroup group)
|
||||
{
|
||||
case TimingControlPoint timing:
|
||||
return new RowAttribute("timing", $"{60000 / timing.BeatLength:n1}bpm {timing.TimeSignature}");
|
||||
InternalChild = fill = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Padding = new MarginPadding(10),
|
||||
Spacing = new Vector2(2)
|
||||
};
|
||||
|
||||
case DifficultyControlPoint difficulty:
|
||||
controlPoints = group.ControlPoints.GetBoundCopy();
|
||||
controlPoints.ItemsAdded += _ => createChildren();
|
||||
controlPoints.ItemsRemoved += _ => createChildren();
|
||||
|
||||
return new RowAttribute("difficulty", $"{difficulty.SpeedMultiplier:n2}x");
|
||||
|
||||
case EffectControlPoint effect:
|
||||
return new RowAttribute("effect", $"{(effect.KiaiMode ? "Kiai " : "")}{(effect.OmitFirstBarLine ? "NoBarLine " : "")}");
|
||||
|
||||
case SampleControlPoint sample:
|
||||
return new RowAttribute("sample", $"{sample.SampleBank} {sample.SampleVolume}%");
|
||||
createChildren();
|
||||
}
|
||||
|
||||
return null;
|
||||
private void createChildren()
|
||||
{
|
||||
fill.ChildrenEnumerable = controlPoints.Select(createAttribute).Where(c => c != null);
|
||||
}
|
||||
|
||||
private Drawable createAttribute(ControlPoint controlPoint)
|
||||
{
|
||||
switch (controlPoint)
|
||||
{
|
||||
case TimingControlPoint timing:
|
||||
return new RowAttribute("timing", $"{60000 / timing.BeatLength:n1}bpm {timing.TimeSignature}");
|
||||
|
||||
case DifficultyControlPoint difficulty:
|
||||
|
||||
return new RowAttribute("difficulty", $"{difficulty.SpeedMultiplier:n2}x");
|
||||
|
||||
case EffectControlPoint effect:
|
||||
return new RowAttribute("effect", $"{(effect.KiaiMode ? "Kiai " : "")}{(effect.OmitFirstBarLine ? "NoBarLine " : "")}");
|
||||
|
||||
case SampleControlPoint sample:
|
||||
return new RowAttribute("sample", $"{sample.SampleBank} {sample.SampleVolume}%");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty);
|
||||
|
37
osu.Game/Screens/Edit/Timing/DifficultySection.cs
Normal file
37
osu.Game/Screens/Edit/Timing/DifficultySection.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Timing
|
||||
{
|
||||
internal class DifficultySection : Section<DifficultyControlPoint>
|
||||
{
|
||||
private OsuSpriteText multiplier;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Flow.AddRange(new[]
|
||||
{
|
||||
multiplier = new OsuSpriteText(),
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ControlPoint.BindValueChanged(point => { multiplier.Text = $"Multiplier: {point.NewValue?.SpeedMultiplier}"; });
|
||||
}
|
||||
|
||||
protected override DifficultyControlPoint CreatePoint()
|
||||
{
|
||||
var reference = Beatmap.Value.Beatmap.ControlPointInfo.DifficultyPointAt(SelectedGroup.Value.Time);
|
||||
|
||||
return new DifficultyControlPoint
|
||||
{
|
||||
SpeedMultiplier = reference.SpeedMultiplier,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
44
osu.Game/Screens/Edit/Timing/EffectSection.cs
Normal file
44
osu.Game/Screens/Edit/Timing/EffectSection.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Timing
|
||||
{
|
||||
internal class EffectSection : Section<EffectControlPoint>
|
||||
{
|
||||
private OsuSpriteText kiai;
|
||||
private OsuSpriteText omitBarLine;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Flow.AddRange(new[]
|
||||
{
|
||||
kiai = new OsuSpriteText(),
|
||||
omitBarLine = new OsuSpriteText(),
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ControlPoint.BindValueChanged(point =>
|
||||
{
|
||||
kiai.Text = $"Kiai: {point.NewValue?.KiaiMode}";
|
||||
omitBarLine.Text = $"Skip Bar Line: {point.NewValue?.OmitFirstBarLine}";
|
||||
});
|
||||
}
|
||||
|
||||
protected override EffectControlPoint CreatePoint()
|
||||
{
|
||||
var reference = Beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(SelectedGroup.Value.Time);
|
||||
|
||||
return new EffectControlPoint
|
||||
{
|
||||
KiaiMode = reference.KiaiMode,
|
||||
OmitFirstBarLine = reference.OmitFirstBarLine
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
44
osu.Game/Screens/Edit/Timing/SampleSection.cs
Normal file
44
osu.Game/Screens/Edit/Timing/SampleSection.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Timing
|
||||
{
|
||||
internal class SampleSection : Section<SampleControlPoint>
|
||||
{
|
||||
private OsuSpriteText bank;
|
||||
private OsuSpriteText volume;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Flow.AddRange(new[]
|
||||
{
|
||||
bank = new OsuSpriteText(),
|
||||
volume = new OsuSpriteText(),
|
||||
});
|
||||
}
|
||||
|
||||
protected override SampleControlPoint CreatePoint()
|
||||
{
|
||||
var reference = Beatmap.Value.Beatmap.ControlPointInfo.SamplePointAt(SelectedGroup.Value.Time);
|
||||
|
||||
return new SampleControlPoint
|
||||
{
|
||||
SampleBank = reference.SampleBank,
|
||||
SampleVolume = reference.SampleVolume,
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ControlPoint.BindValueChanged(point =>
|
||||
{
|
||||
bank.Text = $"Bank: {point.NewValue?.SampleBank}";
|
||||
volume.Text = $"Volume: {point.NewValue?.SampleVolume}";
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
126
osu.Game/Screens/Edit/Timing/Section.cs
Normal file
126
osu.Game/Screens/Edit/Timing/Section.cs
Normal file
@ -0,0 +1,126 @@
|
||||
// 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.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Timing
|
||||
{
|
||||
internal abstract class Section<T> : CompositeDrawable
|
||||
where T : ControlPoint
|
||||
{
|
||||
private OsuCheckbox checkbox;
|
||||
private Container content;
|
||||
|
||||
protected FillFlowContainer Flow { get; private set; }
|
||||
|
||||
protected Bindable<T> ControlPoint { get; } = new Bindable<T>();
|
||||
|
||||
private const float header_height = 20;
|
||||
|
||||
[Resolved]
|
||||
protected IBindable<WorkingBeatmap> Beatmap { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
protected Bindable<ControlPointGroup> SelectedGroup { get; private set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeDuration = 200;
|
||||
AutoSizeEasing = Easing.OutQuint;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Masking = true;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colours.Gray1,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = header_height,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
checkbox = new OsuCheckbox
|
||||
{
|
||||
LabelText = typeof(T).Name.Replace(typeof(ControlPoint).Name, string.Empty)
|
||||
}
|
||||
}
|
||||
},
|
||||
content = new Container
|
||||
{
|
||||
Y = header_height,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colours.Gray2,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
Flow = new FillFlowContainer
|
||||
{
|
||||
Padding = new MarginPadding(10),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
checkbox.Current.BindValueChanged(selected =>
|
||||
{
|
||||
if (selected.NewValue)
|
||||
{
|
||||
if (SelectedGroup.Value == null)
|
||||
{
|
||||
checkbox.Current.Value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ControlPoint.Value == null)
|
||||
SelectedGroup.Value.Add(ControlPoint.Value = CreatePoint());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ControlPoint.Value != null)
|
||||
{
|
||||
SelectedGroup.Value.Remove(ControlPoint.Value);
|
||||
ControlPoint.Value = null;
|
||||
}
|
||||
}
|
||||
|
||||
content.BypassAutoSizeAxes = selected.NewValue ? Axes.None : Axes.Y;
|
||||
}, true);
|
||||
|
||||
SelectedGroup.BindValueChanged(points =>
|
||||
{
|
||||
ControlPoint.Value = points.NewValue?.ControlPoints.OfType<T>().FirstOrDefault();
|
||||
checkbox.Current.Value = ControlPoint.Value != null;
|
||||
}, true);
|
||||
}
|
||||
|
||||
protected abstract T CreatePoint();
|
||||
}
|
||||
}
|
44
osu.Game/Screens/Edit/Timing/TimingSection.cs
Normal file
44
osu.Game/Screens/Edit/Timing/TimingSection.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Timing
|
||||
{
|
||||
internal class TimingSection : Section<TimingControlPoint>
|
||||
{
|
||||
private OsuSpriteText bpm;
|
||||
private OsuSpriteText timeSignature;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Flow.AddRange(new[]
|
||||
{
|
||||
bpm = new OsuSpriteText(),
|
||||
timeSignature = new OsuSpriteText(),
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ControlPoint.BindValueChanged(point =>
|
||||
{
|
||||
bpm.Text = $"BPM: {point.NewValue?.BeatLength}";
|
||||
timeSignature.Text = $"Signature: {point.NewValue?.TimeSignature}";
|
||||
});
|
||||
}
|
||||
|
||||
protected override TimingControlPoint CreatePoint()
|
||||
{
|
||||
var reference = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(SelectedGroup.Value.Time);
|
||||
|
||||
return new TimingControlPoint
|
||||
{
|
||||
BeatLength = reference.BeatLength,
|
||||
TimeSignature = reference.TimeSignature
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user