Hoist reference replacement logic to overlay level

This commit is contained in:
Bartłomiej Dach
2022-05-11 19:30:06 +02:00
parent 05a21fbbe0
commit 11ae1da65a
2 changed files with 64 additions and 137 deletions

View File

@ -207,14 +207,9 @@ namespace osu.Game.Overlays.Mods
{
updateMultiplier();
updateCustomisation(val);
updateSelectionFromBindable();
updateFromExternalSelection();
}, true);
foreach (var column in columnFlow.Columns)
{
column.SelectionChangedByUser += updateBindableFromSelection;
}
customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true);
// Start scrolled slightly to the right to give the user a sense that
@ -248,7 +243,6 @@ namespace osu.Game.Overlays.Mods
{
var column = CreateModColumn(modType, toggleKeys).With(column =>
{
column.Filter = IsValidMod;
// spacing applied here rather than via `columnFlow.Spacing` to avoid uneven gaps when some of the columns are hidden.
column.Margin = new MarginPadding { Right = 10 };
});
@ -286,6 +280,9 @@ namespace osu.Game.Overlays.Mods
.Select(mod => new ModState(mod.DeepClone()))
.ToArray();
foreach (var modState in modStates)
modState.Active.BindValueChanged(_ => updateFromInternalSelection());
localAvailableMods[modType] = modStates;
}
@ -362,20 +359,50 @@ namespace osu.Game.Overlays.Mods
TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic);
}
private void updateSelectionFromBindable()
{
// `SelectedMods` may contain mod references that come from external sources.
// to ensure isolation, first pull in the potentially-external change into the mod columns...
foreach (var column in columnFlow.Columns)
column.SetSelection(SelectedMods.Value);
/// <summary>
/// This flag helps to determine the source of changes to <see cref="SelectedMods"/>.
/// If the value is false, then <see cref="SelectedMods"/> are changing due to a user selection on the UI.
/// If the value is true, then <see cref="SelectedMods"/> are changing due to an external <see cref="SelectedMods"/> change.
/// </summary>
private bool externalSelectionUpdateInProgress;
// and then, when done, replace the potentially-external mod references in `SelectedMods` with ones we own.
updateBindableFromSelection();
private void updateFromExternalSelection()
{
externalSelectionUpdateInProgress = true;
var newSelection = new List<Mod>();
foreach (var modState in localAvailableMods.SelectMany(pair => pair.Value))
{
var matchingSelectedMod = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == modState.Mod.GetType());
if (matchingSelectedMod != null)
{
modState.Mod.CopyFrom(matchingSelectedMod);
modState.Active.Value = true;
newSelection.Add(modState.Mod);
}
else
{
modState.Mod.ResetSettingsToDefaults();
modState.Active.Value = false;
}
}
SelectedMods.Value = newSelection;
externalSelectionUpdateInProgress = false;
}
private void updateBindableFromSelection()
private void updateFromInternalSelection()
{
var candidateSelection = columnFlow.Columns.SelectMany(column => column.SelectedMods).ToArray();
if (externalSelectionUpdateInProgress)
return;
var candidateSelection = localAvailableMods.SelectMany(pair => pair.Value)
.Where(modState => modState.Active.Value)
.Select(modState => modState.Mod)
.ToArray();
// the following guard intends to check cases where we've already replaced potentially-external mod references with our own and avoid endless recursion.
// TODO: replace custom comparer with System.Collections.Generic.ReferenceEqualityComparer when fully on .NET 6
@ -406,10 +433,12 @@ namespace osu.Game.Overlays.Mods
{
var column = columnFlow[i].Column;
double delay = column.AllFiltered.Value ? 0 : nonFilteredColumnCount * 30;
double duration = column.AllFiltered.Value ? 0 : fade_in_duration;
bool allFiltered = column.AvailableMods.All(modState => modState.Filtered.Value);
double delay = allFiltered ? 0 : nonFilteredColumnCount * 30;
double duration = allFiltered ? 0 : fade_in_duration;
float startingYPosition = 0;
if (!column.AllFiltered.Value)
if (!allFiltered)
startingYPosition = nonFilteredColumnCount % 2 == 0 ? -distance : distance;
column.TopLevelContent
@ -418,7 +447,7 @@ namespace osu.Game.Overlays.Mods
.MoveToY(0, duration, Easing.OutQuint)
.FadeIn(duration, Easing.OutQuint);
if (!column.AllFiltered.Value)
if (!allFiltered)
nonFilteredColumnCount += 1;
}
}
@ -439,9 +468,11 @@ namespace osu.Game.Overlays.Mods
{
var column = columnFlow[i].Column;
double duration = column.AllFiltered.Value ? 0 : fade_out_duration;
bool allFiltered = column.AvailableMods.All(modState => modState.Filtered.Value);
double duration = allFiltered ? 0 : fade_out_duration;
float newYPosition = 0;
if (!column.AllFiltered.Value)
if (!allFiltered)
newYPosition = nonFilteredColumnCount % 2 == 0 ? -distance : distance;
column.FlushPendingSelections();
@ -449,7 +480,7 @@ namespace osu.Game.Overlays.Mods
.MoveToY(newYPosition, duration, Easing.OutQuint)
.FadeOut(duration, Easing.OutQuint);
if (!column.AllFiltered.Value)
if (!allFiltered)
nonFilteredColumnCount += 1;
}
}
@ -593,8 +624,8 @@ namespace osu.Game.Overlays.Mods
protected override void LoadComplete()
{
base.LoadComplete();
Active.BindValueChanged(_ => updateState());
Column.AllFiltered.BindValueChanged(_ => updateState(), true);
Active.BindValueChanged(_ => updateState(), true);
FinishTransforms();
}
@ -604,8 +635,6 @@ namespace osu.Game.Overlays.Mods
{
Colour4 targetColour;
Column.Alpha = Column.AllFiltered.Value ? 0 : 1;
if (Column.Active.Value)
targetColour = Colour4.White;
else