From b4df2d6d43f96659c6687badc4c739770957ebfa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 03:46:09 +0300 Subject: [PATCH 1/8] Add method for copying properties from another mod --- osu.Game/Rulesets/Mods/Mod.cs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b8dc7a2661..236bf9ff00 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -131,22 +131,31 @@ namespace osu.Game.Rulesets.Mods /// public virtual Mod CreateCopy() { - var copy = (Mod)Activator.CreateInstance(GetType()); + var result = (Mod)Activator.CreateInstance(GetType(), true); + result.CopyFrom(this); + return result; + } + + /// + /// Copies properties of given mod into here, through changing value. + /// + /// The mod to copy properties from. + public void CopyFrom(Mod them) + { + if (them.GetType() != GetType()) + throw new ArgumentException($"Expected mod of type {GetType()}, got {them.GetType()}.", nameof(them)); - // Copy bindable values across foreach (var (_, prop) in this.GetSettingsSourceProperties()) { - var origBindable = prop.GetValue(this); - var copyBindable = prop.GetValue(copy); + var ourBindable = prop.GetValue(this); + var theirBindable = prop.GetValue(them); // The bindables themselves are readonly, so the value must be transferred through the Bindable.Value property. - var valueProperty = origBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); + var valueProperty = theirBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); Debug.Assert(valueProperty != null); - valueProperty.SetValue(copyBindable, valueProperty.GetValue(origBindable)); + valueProperty.SetValue(ourBindable, valueProperty.GetValue(theirBindable)); } - - return copy; } public bool Equals(IMod other) => GetType() == other?.GetType(); From 2ce9599957d50f5c9b8220cf7d3105372191a6d4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 03:47:13 +0300 Subject: [PATCH 2/8] Copy selected mods properties into overlay's buttons --- osu.Game/Overlays/Mods/ModSection.cs | 24 +++++++++++++++------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 0107f94dcf..86da179064 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -127,18 +127,28 @@ namespace osu.Game.Overlays.Mods } /// - /// Select one or more mods in this section and deselects all other ones. + /// Updates all buttons with the given list of selected mods. /// - /// The types of s which should be selected. - public void SelectTypes(IEnumerable modTypes) + /// The types of s to select. + public void UpdateSelectedMods(IReadOnlyList newSelectedMods) { foreach (var button in buttons) { - int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t == m.GetType())); + int index = -1; - if (i >= 0) - button.SelectAt(i); - else + foreach (var mod in newSelectedMods) + { + index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); + if (index < 0) + continue; + + var buttonMod = button.Mods[index]; + buttonMod.CopyFrom(mod); + button.SelectAt(index); + break; + } + + if (index < 0) button.Deselect(); } } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 34f5c70adb..c1622548f5 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -409,7 +409,7 @@ namespace osu.Game.Overlays.Mods private void selectedModsChanged(ValueChangedEvent> mods) { foreach (var section in ModSectionsContainer.Children) - section.SelectTypes(mods.NewValue.Select(m => m.GetType()).ToList()); + section.UpdateSelectedMods(mods.NewValue); updateMods(); } From ee664ad57117fbdd01dd38a3025a621e69bf7eca Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 03:47:18 +0300 Subject: [PATCH 3/8] Add test coverage --- .../UserInterface/TestSceneModSelectOverlay.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 6f083f4ab6..0d0acbb8f4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -131,6 +131,18 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0); } + [Test] + public void TestExternallySetCustomizedMod() + { + AddStep("set customized mod externally", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } }); + + AddAssert("ensure button is selected and customized accordingly", () => + { + var button = modSelect.GetModButton(SelectedMods.Value.Single()); + return ((OsuModDoubleTime)button.SelectedMod).SpeedChange.Value == 1.01; + }); + } + private void testSingleMod(Mod mod) { selectNext(mod); From a031c8e0b62f3933e23e049dc75633f6c4603aef Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 15:34:09 +0300 Subject: [PATCH 4/8] Apply documentation suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Overlays/Mods/ModSection.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 86da179064..e29ed4f5c4 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -129,7 +129,7 @@ namespace osu.Game.Overlays.Mods /// /// Updates all buttons with the given list of selected mods. /// - /// The types of s to select. + /// The new list of selected mods to select. public void UpdateSelectedMods(IReadOnlyList newSelectedMods) { foreach (var button in buttons) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 236bf9ff00..6e83299d63 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Mods } /// - /// Copies properties of given mod into here, through changing value. + /// Copies mod setting values from into this instance. /// /// The mod to copy properties from. public void CopyFrom(Mod them) From c1a1e3acc5aa199442ce7a96058e1eaae2bc7dd9 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 15:40:40 +0300 Subject: [PATCH 5/8] Revert drive-by changes --- osu.Game/Rulesets/Mods/Mod.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 6e83299d63..3f1f16e561 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -131,7 +131,7 @@ namespace osu.Game.Rulesets.Mods /// public virtual Mod CreateCopy() { - var result = (Mod)Activator.CreateInstance(GetType(), true); + var result = (Mod)Activator.CreateInstance(GetType()); result.CopyFrom(this); return result; } @@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.Mods var theirBindable = prop.GetValue(them); // The bindables themselves are readonly, so the value must be transferred through the Bindable.Value property. - var valueProperty = theirBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); + var valueProperty = ourBindable.GetType().GetProperty(nameof(Bindable.Value), BindingFlags.Public | BindingFlags.Instance); Debug.Assert(valueProperty != null); valueProperty.SetValue(ourBindable, valueProperty.GetValue(theirBindable)); From 988f9b98a153d95b5f6de42dc3782d08912c125f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 1 Jan 2021 16:16:00 +0300 Subject: [PATCH 6/8] Split button mods updating to private method --- osu.Game/Overlays/Mods/ModSection.cs | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index e29ed4f5c4..573d1e5355 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -133,24 +133,24 @@ namespace osu.Game.Overlays.Mods public void UpdateSelectedMods(IReadOnlyList newSelectedMods) { foreach (var button in buttons) + updateButtonMods(button, newSelectedMods); + } + + private void updateButtonMods(ModButton button, IReadOnlyList newSelectedMods) + { + foreach (var mod in newSelectedMods) { - int index = -1; - - foreach (var mod in newSelectedMods) - { - index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); - if (index < 0) - continue; - - var buttonMod = button.Mods[index]; - buttonMod.CopyFrom(mod); - button.SelectAt(index); - break; - } - + var index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); if (index < 0) - button.Deselect(); + continue; + + var buttonMod = button.Mods[index]; + buttonMod.CopyFrom(mod); + button.SelectAt(index); + return; } + + button.Deselect(); } protected ModSection() From 0aad0c7c6c5dd363bd5bb75d2eb4cc5c35a3804f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 9 Jan 2021 00:30:12 +0300 Subject: [PATCH 7/8] Target logic at `this` and adjust variables --- osu.Game/Rulesets/Mods/Mod.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 7fe9a06597..e72e9a004f 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -144,12 +144,12 @@ namespace osu.Game.Rulesets.Mods foreach (var (_, prop) in this.GetSettingsSourceProperties()) { - var origBindable = (IBindable)prop.GetValue(this); - var copyBindable = (IBindable)prop.GetValue(copy); + var targetBindable = (IBindable)prop.GetValue(this); + var sourceBindable = (IBindable)prop.GetValue(them); // we only care about changes that have been made away from defaults. - if (!origBindable.IsDefault) - copy.CopyAdjustedSetting(copyBindable, origBindable); + if (!sourceBindable.IsDefault) + CopyAdjustedSetting(targetBindable, sourceBindable); } } From 4b4adc927cb49a166f9f13c093c318afbbe18f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 10 Jan 2021 15:35:53 +0100 Subject: [PATCH 8/8] Rename param to match method body --- osu.Game/Rulesets/Mods/Mod.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index e72e9a004f..3a8717e678 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -134,18 +134,18 @@ namespace osu.Game.Rulesets.Mods } /// - /// Copies mod setting values from into this instance. + /// Copies mod setting values from into this instance. /// - /// The mod to copy properties from. - public void CopyFrom(Mod them) + /// The mod to copy properties from. + public void CopyFrom(Mod source) { - if (them.GetType() != GetType()) - throw new ArgumentException($"Expected mod of type {GetType()}, got {them.GetType()}.", nameof(them)); + if (source.GetType() != GetType()) + throw new ArgumentException($"Expected mod of type {GetType()}, got {source.GetType()}.", nameof(source)); foreach (var (_, prop) in this.GetSettingsSourceProperties()) { var targetBindable = (IBindable)prop.GetValue(this); - var sourceBindable = (IBindable)prop.GetValue(them); + var sourceBindable = (IBindable)prop.GetValue(source); // we only care about changes that have been made away from defaults. if (!sourceBindable.IsDefault)