1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-12 09:47:18 +08:00

Make sample popover change properties of all samples in multiple selection

Closes https://github.com/ppy/osu/issues/28916.

The previous behaviour *may* have been intended, but it was honestly
quite baffling. This seems like a saner variant.
This commit is contained in:
Bartłomiej Dach 2024-07-23 14:34:48 +02:00
parent 55382a4ba6
commit 1ed7e4b075
No known key found for this signature in database
2 changed files with 44 additions and 15 deletions

View File

@ -34,12 +34,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{ {
private readonly int nodeIndex; private readonly int nodeIndex;
protected override IList<HitSampleInfo> GetRelevantSamples(HitObject ho) protected override IEnumerable<(HitObject hitObject, IList<HitSampleInfo> samples)> GetRelevantSamples(HitObject[] hitObjects)
{ {
if (ho is not IHasRepeats hasRepeats) if (hitObjects.Length > 1 || hitObjects[0] is not IHasRepeats hasRepeats)
return ho.Samples; return base.GetRelevantSamples(hitObjects);
return nodeIndex < hasRepeats.NodeSamples.Count ? hasRepeats.NodeSamples[nodeIndex] : ho.Samples; return [(hitObjects[0], nodeIndex < hasRepeats.NodeSamples.Count ? hasRepeats.NodeSamples[nodeIndex] : hitObjects[0].Samples)];
} }
public NodeSampleEditPopover(HitObject hitObject, int nodeIndex) public NodeSampleEditPopover(HitObject hitObject, int nodeIndex)

View File

@ -21,6 +21,7 @@ using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Components.TernaryButtons;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Screens.Edit.Timing; using osu.Game.Screens.Edit.Timing;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -106,15 +107,34 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private FillFlowContainer togglesCollection = null!; private FillFlowContainer togglesCollection = null!;
private HitObject[] relevantObjects = null!; private HitObject[] relevantObjects = null!;
private IList<HitSampleInfo>[] allRelevantSamples = null!; private (HitObject hitObject, IList<HitSampleInfo> samples)[] allRelevantSamples = null!;
/// <summary> /// <summary>
/// Gets the sub-set of samples relevant to this sample point piece. /// Gets the sub-set of samples relevant to this sample point piece.
/// For example, to edit node samples this should return the samples at the index of the node. /// For example, to edit node samples this should return the samples at the index of the node.
/// </summary> /// </summary>
/// <param name="ho">The hit object to get the relevant samples from.</param> /// <param name="hitObjects">The hit objects to get the relevant samples from.</param>
/// <returns>The relevant list of samples.</returns> /// <returns>The relevant list of samples.</returns>
protected virtual IList<HitSampleInfo> GetRelevantSamples(HitObject ho) => ho.Samples; protected virtual IEnumerable<(HitObject hitObject, IList<HitSampleInfo> samples)> GetRelevantSamples(HitObject[] hitObjects)
{
if (hitObjects.Length == 1)
{
yield return (hitObjects[0], hitObjects[0].Samples);
yield break;
}
foreach (var ho in hitObjects)
{
yield return (ho, ho.Samples);
if (ho is IHasRepeats hasRepeats)
{
foreach (var node in hasRepeats.NodeSamples)
yield return (ho, node);
}
}
}
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private EditorBeatmap beatmap { get; set; } = null!; private EditorBeatmap beatmap { get; set; } = null!;
@ -172,7 +192,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
// if the piece belongs to a currently selected object, assume that the user wants to change all selected objects. // if the piece belongs to a currently selected object, assume that the user wants to change all selected objects.
// if the piece belongs to an unselected object, operate on that object alone, independently of the selection. // if the piece belongs to an unselected object, operate on that object alone, independently of the selection.
relevantObjects = (beatmap.SelectedHitObjects.Contains(hitObject) ? beatmap.SelectedHitObjects : hitObject.Yield()).ToArray(); relevantObjects = (beatmap.SelectedHitObjects.Contains(hitObject) ? beatmap.SelectedHitObjects : hitObject.Yield()).ToArray();
allRelevantSamples = relevantObjects.Select(GetRelevantSamples).ToArray(); allRelevantSamples = GetRelevantSamples(relevantObjects).ToArray();
// even if there are multiple objects selected, we can still display sample volume or bank if they all have the same value. // even if there are multiple objects selected, we can still display sample volume or bank if they all have the same value.
int? commonVolume = getCommonVolume(); int? commonVolume = getCommonVolume();
@ -214,9 +234,19 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
togglesCollection.AddRange(createTernaryButtons().Select(b => new DrawableTernaryButton(b) { RelativeSizeAxes = Axes.None, Size = new Vector2(40, 40) })); togglesCollection.AddRange(createTernaryButtons().Select(b => new DrawableTernaryButton(b) { RelativeSizeAxes = Axes.None, Size = new Vector2(40, 40) }));
} }
private string? getCommonBank() => allRelevantSamples.Select(GetBankValue).Distinct().Count() == 1 ? GetBankValue(allRelevantSamples.First()) : null; private string? getCommonBank() => allRelevantSamples.Select(h => GetBankValue(h.samples)).Distinct().Count() == 1
private string? getCommonAdditionBank() => allRelevantSamples.Select(GetAdditionBankValue).Where(o => o is not null).Distinct().Count() == 1 ? GetAdditionBankValue(allRelevantSamples.First()) : null; ? GetBankValue(allRelevantSamples.First().samples)
private int? getCommonVolume() => allRelevantSamples.Select(GetVolumeValue).Distinct().Count() == 1 ? GetVolumeValue(allRelevantSamples.First()) : null; : null;
private string? getCommonAdditionBank()
{
string[] additionBanks = allRelevantSamples.Select(h => GetAdditionBankValue(h.samples)).Where(o => o is not null).Cast<string>().Distinct().ToArray();
return additionBanks.Length == 1 ? additionBanks[0] : null;
}
private int? getCommonVolume() => allRelevantSamples.Select(h => GetVolumeValue(h.samples)).Distinct().Count() == 1
? GetVolumeValue(allRelevantSamples.First().samples)
: null;
private void updatePrimaryBankState() private void updatePrimaryBankState()
{ {
@ -231,7 +261,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
additionBank.PlaceholderText = string.IsNullOrEmpty(commonAdditionBank) ? "(multiple)" : string.Empty; additionBank.PlaceholderText = string.IsNullOrEmpty(commonAdditionBank) ? "(multiple)" : string.Empty;
additionBank.Current.Value = commonAdditionBank; additionBank.Current.Value = commonAdditionBank;
bool anyAdditions = allRelevantSamples.Any(o => o.Any(s => s.Name != HitSampleInfo.HIT_NORMAL)); bool anyAdditions = allRelevantSamples.Any(o => o.samples.Any(s => s.Name != HitSampleInfo.HIT_NORMAL));
if (anyAdditions) if (anyAdditions)
additionBank.Show(); additionBank.Show();
else else
@ -247,9 +277,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{ {
beatmap.BeginChange(); beatmap.BeginChange();
foreach (var relevantHitObject in relevantObjects) foreach (var (relevantHitObject, relevantSamples) in GetRelevantSamples(relevantObjects))
{ {
var relevantSamples = GetRelevantSamples(relevantHitObject);
updateAction(relevantHitObject, relevantSamples); updateAction(relevantHitObject, relevantSamples);
beatmap.Update(relevantHitObject); beatmap.Update(relevantHitObject);
} }
@ -333,7 +362,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{ {
foreach ((string sampleName, var bindable) in selectionSampleStates) foreach ((string sampleName, var bindable) in selectionSampleStates)
{ {
bindable.Value = SelectionHandler<HitObject>.GetStateFromSelection(relevantObjects, h => GetRelevantSamples(h).Any(s => s.Name == sampleName)); bindable.Value = SelectionHandler<HitObject>.GetStateFromSelection(GetRelevantSamples(relevantObjects), h => h.samples.Any(s => s.Name == sampleName));
} }
} }